source: subversion/sites/namefinder/php/canon.php @ 4135

Last change on this file since 4135 was 4134, checked in by david, 12 years ago

Main application files

File size: 32.7 KB
Line 
1<?php
2
3/* This class provides services to canonicalise and generalise search terms */
4
5class canon {
6
7  /* static */ function canonical($term, $alternates=FALSE) {
8
9    /* produces a string which is a canonical form of the name. Names are converted like
10       this when indexed, so that when a similar name is converted on search, it can
11       also be converted to canonical form to compare with the canonical index.
12
13       term: a name being indexed or sought, for example 'Ben Nevis' or 'Newmarket Road [A1304]'
14       alternates: whether to consider replacing a diacritical character
15         with multiple alternate replacements, for example Danish aring character with aa and a
16         (we normally only do this on indexing, not on lookup).
17
18       The canonical form stores each word surrounded by semcolons
19       (that is, not a separator, so there will be two between each one
20       as well as one at each end. This makes it easy to search with
21       wildcards without worrying about the start and end of
22       strings. Alternates in the index are separated by #, and there
23       is a # at the start and end. This makes it possible to search
24       for exact matches (see below) or partial matches while not
25       confusing words from alternate parts. # arises in two ways: a
26       square bracket in the name - so 'Newmarket Road [A1134]' becomes
27       '#;newmarket;;road;#;A1134;#' - and when there are alternate substitutions
28       for diacritical caharacters (see below) - so 'M<u-umlaut>nchen' becomes
29       '#;munchen;#;muenchen;' */
30
31    static $replacements = '';
32
33    /* The replacements table maps UTF characters (multiple byte keys) to ascii equivalents
34       so that characters such as u-umlaut can be matched by u and ue. There are multiple
35       tables because some characters have more than one functional replacement
36       (as for u-umlaut). We store multiple canonical forms, but search only on one (so
37       M<u-umlaut>nchen is stored as '#;munchen;#;muenchen;#' which means munchen or
38       muenchen as search strings will match one or the ther, and M<u-umlaut>nchen as
39       a search string will match the first.
40
41       see http://www.utf8-chartable.de/unicode-utf8-table.pl for a utf-8 list
42    */
43
44    if (empty($replacements)) {
45      $replacements = array(
46        array(
47          // ligatures
48          chr(0xc3).chr(0x86) => 'ae', // cap
49          chr(0xc3).chr(0xa6) => 'ae',
50          chr(0xc5).chr(0x92) => 'oe', // cap
51          chr(0xc3).chr(0x86) => 'oe',
52          chr(0xc3).chr(0x9f) => 'ss', // german B
53          chr(0xc5).chr(0x8a) => 'ng', // cap
54          chr(0xc5).chr(0x8b) => 'ng',
55          chr(0xe1).chr(0xb5).chr(0xab) => 'ue',
56          chr(0xef).chr(0xac).chr(0x80) => 'ff',
57          chr(0xef).chr(0xac).chr(0x81) => 'fi',
58          chr(0xef).chr(0xac).chr(0x82) => 'fl',
59          chr(0xef).chr(0xac).chr(0x83) => 'ffi',
60          chr(0xef).chr(0xac).chr(0x84) => 'ffl',
61          chr(0xef).chr(0xac).chr(0x85) => 'ft',
62          chr(0xef).chr(0xac).chr(0x86) => 'st',
63
64          chr(0x00) => '',
65          chr(0x01) => '',
66          chr(0x02) => '',
67          chr(0x03) => '',
68          chr(0x04) => '',
69          chr(0x05) => '',
70          chr(0x06) => '',
71          chr(0x07) => '',
72          chr(0x08) => '',
73          chr(0x09) => '',
74          chr(0x0A) => '',
75          chr(0x0B) => '',
76          chr(0x0C) => '',
77          chr(0x0D) => '',
78          chr(0x0E) => '',
79          chr(0x0F) => '',
80
81          chr(0x10) => '',
82          chr(0x11) => '',
83          chr(0x12) => '',
84          chr(0x13) => '',
85          chr(0x14) => '',
86          chr(0x15) => '',
87          chr(0x16) => '',
88          chr(0x17) => '',
89          chr(0x18) => '',
90          chr(0x19) => '',
91          chr(0x1A) => '',
92          chr(0x1B) => '',
93          chr(0x1C) => '',
94          chr(0x1D) => '',
95          chr(0x1E) => '',
96          chr(0x1F) => '',
97
98          chr(0x20) => ';;', // space
99          chr(0x21) => '', // !
100          chr(0x22) => '',  // "
101          chr(0x23) => '', // #
102          chr(0x24) => '', // $
103          chr(0x25) => '',  // %
104          chr(0x26) => ';and;', // &
105          chr(0x27) => '',  // '
106          chr(0x28) => '', // (
107          chr(0x29) => '', // )
108          chr(0x2A) => '', // *
109          chr(0x2B) => '',  // +
110          chr(0x2C) => '', // ,
111          chr(0x2D) => ';;',  // -
112          chr(0x2E) => '', // .
113          chr(0x2F) => ';;', // /
114
115          chr(0x3A) => ';;', // :
116          chr(0x3B) => ';;', // ;
117          chr(0x3C) => '',  // <
118          chr(0x3D) => ';;', // =
119          chr(0x3E) => '',  // >
120          chr(0x3F) => '', // ?
121
122          chr(0x41) => 'a', // cap A
123          chr(0x42) => 'b',
124          chr(0x43) => 'c',
125          chr(0x44) => 'd',
126          chr(0x45) => 'e',
127          chr(0x46) => 'f',
128          chr(0x47) => 'g',
129          chr(0x48) => 'h',
130          chr(0x49) => 'i',
131          chr(0x4a) => 'j',
132          chr(0x4b) => 'k',
133          chr(0x4c) => 'l',
134          chr(0x4d) => 'm',
135          chr(0x4e) => 'n',
136          chr(0x4f) => 'o',
137          chr(0x50) => 'p',
138          chr(0x51) => 'q',
139          chr(0x52) => 'r',
140          chr(0x53) => 's',
141          chr(0x54) => 't',
142          chr(0x55) => 'u',
143          chr(0x56) => 'v',
144          chr(0x57) => 'w',
145          chr(0x58) => 'x',
146          chr(0x59) => 'y',
147          chr(0x5A) => 'z', // cap Z
148
149          chr(0x5B) => ';#;', // [ - forces separation of components for exact match
150          chr(0x5C) => '',  // backslash
151          chr(0x5D) => '', // ]
152          chr(0x5E) => '',  // hat
153          chr(0x5F) => '', // _
154
155          chr(0x60) => '', // backtick
156          chr(0x7B) => '', // {
157          chr(0x7C) => ';;',  // |
158          chr(0x7D) => '', // }
159          chr(0x7E) => '',  // tilde
160          chr(0x7F) => '', // unused ...
161
162          chr(0xc2).chr(0xA0) => ';;', // nbsp
163          chr(0xc2).chr(0xA1) => '',  // upside down exclaim
164          chr(0xc2).chr(0xA2) => '',  // c stroke
165          chr(0xc2).chr(0xA3) => '',  // pound
166          chr(0xc2).chr(0xA4) => '',  //
167          chr(0xc2).chr(0xA5) => '',  // yen
168          chr(0xc2).chr(0xA6) => '',  // double bar
169          chr(0xc2).chr(0xA7) => '',  // para
170          chr(0xc2).chr(0xA8) => '',  // umlaut
171          chr(0xc2).chr(0xA9) => '',  // copyright
172          chr(0xc2).chr(0xAA) => '',
173          chr(0xc2).chr(0xAB) => '',  // laquo
174          chr(0xc2).chr(0xAC) => '',  // hook
175          chr(0xc2).chr(0xAD) => '',  // SHY
176          chr(0xc2).chr(0xAE) => '',  // registered
177          chr(0xc2).chr(0xAF) => '',  // bar accent
178
179          chr(0xc2).chr(0xB0) => '',  // degrees
180          chr(0xc2).chr(0xB1) => '',  // plus or minus
181          chr(0xc2).chr(0xB2) => '',  // squared
182          chr(0xc2).chr(0xB3) => '',  // cubed
183          chr(0xc2).chr(0xB4) => '',  // rsquo ??
184          chr(0xc2).chr(0xB5) => '',  // mu
185          chr(0xc2).chr(0xB6) => '',  // para
186          chr(0xc2).chr(0xB7) => '',  // dot
187          chr(0xc2).chr(0xB8) => '',  // cedilla
188          chr(0xc2).chr(0xB9) => '',  // power 1
189          chr(0xc2).chr(0xBA) => '',  // power 0
190          chr(0xc2).chr(0xBB) => '' ,  // raquo
191          chr(0xc2).chr(0xBC) => '',  // quarter
192          chr(0xc2).chr(0xBD) => '',  // half
193          chr(0xc2).chr(0xBE) => '',  // three quarters
194          chr(0xc2).chr(0xBF) => '',  // upside down ? mark
195
196          chr(0xc3).chr(0x80) => 'a',  // A grave
197          chr(0xc3).chr(0x81) => 'a',  // A acute
198          chr(0xc3).chr(0x82) => 'a',  // A circumflex
199          chr(0xc3).chr(0x83) => 'a',  // A tilde
200          chr(0xc3).chr(0x84) => 'a',  // A umlaut
201          chr(0xc3).chr(0x85) => 'aa', // A ring
202          chr(0xc3).chr(0x86) => 'ae', // AE dipthong
203          chr(0xc3).chr(0x87) => 'c',  // C cedilla
204          chr(0xc3).chr(0x88) => 'e',  // E grave
205          chr(0xc3).chr(0x89) => 'e',  // E acute
206          chr(0xc3).chr(0x8A) => 'e',  // E circumflex
207          chr(0xc3).chr(0x8B) => 'e',  // E double dot
208          chr(0xc3).chr(0x8C) => 'i',  // I grave
209          chr(0xc3).chr(0x8D) => 'i',  // I acute
210          chr(0xc3).chr(0x8E) => 'i',  // I circumflex
211          chr(0xc3).chr(0x8F) => 'i',  // I umlaut
212
213          chr(0xc3).chr(0x90) => 'd',  // D bar (eth)
214          chr(0xc3).chr(0x91) => 'n',  // N tilde
215          chr(0xc3).chr(0x92) => 'o',  // O grave
216          chr(0xc3).chr(0x93) => 'o',  // O acute
217          chr(0xc3).chr(0x94) => 'o',  // O circumflex
218          chr(0xc3).chr(0x95) => 'o',  // O tilde
219          chr(0xc3).chr(0x96) => 'o',  // O umlaut
220          chr(0xc3).chr(0x97) => '',   // multiply
221          chr(0xc3).chr(0x98) => 'o',  // O with slash
222          chr(0xc3).chr(0x99) => 'u',  // U grave
223          chr(0xc3).chr(0x9A) => 'u',  // U acute
224          chr(0xc3).chr(0x9B) => 'u',  // U circumflex
225          chr(0xc3).chr(0x9C) => 'u',  // U umlaut
226          chr(0xc3).chr(0x9D) => 'y',  // y acute
227          chr(0xc3).chr(0x9E) => 'th', // D with long straight edge- thorn
228          chr(0xc3).chr(0x9F) => 'ss', // german "B" like
229
230          chr(0xc3).chr(0xA0) => 'a',  // a grave
231          chr(0xc3).chr(0xA1) => 'a',  // a acute
232          chr(0xc3).chr(0xA2) => 'a',  // a circumflex
233          chr(0xc3).chr(0xA3) => 'a',  // a tilde
234          chr(0xc3).chr(0xA4) => 'a',  // a umlaut
235          chr(0xc3).chr(0xA5) => 'aa', // a ring
236          chr(0xc3).chr(0xA6) => 'ae', // dipthong
237          chr(0xc3).chr(0xA7) => 'c',  // c cedilla
238          chr(0xc3).chr(0xA8) => 'e',  // e grave
239          chr(0xc3).chr(0xA9) => 'e',  // e acute
240          chr(0xc3).chr(0xAA) => 'e',  // e circumflex
241          chr(0xc3).chr(0xAB) => 'e',  // e umlaut
242          chr(0xc3).chr(0xAC) => 'i',  // i grave
243          chr(0xc3).chr(0xAD) => 'i',  // i acute
244          chr(0xc3).chr(0xAE) => 'i',  // i circumflex
245          chr(0xc3).chr(0xAF) => 'i',  // i umlaut
246
247          chr(0xc3).chr(0xB0) => 'd',  // lower case eth
248          chr(0xc3).chr(0xB1) => 'n',  // n tilde
249          chr(0xc3).chr(0xB2) => 'o',  // o grave
250          chr(0xc3).chr(0xB3) => 'o',  // o acute
251          chr(0xc3).chr(0xB4) => 'o',  // o circumflex
252          chr(0xc3).chr(0xB5) => 'o',  // o tilde
253          chr(0xc3).chr(0xB6) => 'o',  // o umlaut
254          chr(0xc3).chr(0xB7) => '',   // divide
255          chr(0xc3).chr(0xB8) => 'o',  // o slash (scandinavian)
256          chr(0xc3).chr(0xB9) => 'u',  // u grave
257          chr(0xc3).chr(0xBA) => 'u',  // u acute
258          chr(0xc3).chr(0xBB) => 'u',  // u circumflex
259          chr(0xc3).chr(0xBC) => 'u',  // u umlaut
260          chr(0xc3).chr(0xBD) => 'y',  // y acute
261          chr(0xc3).chr(0xBE) => 'th', // thorn
262          chr(0xc3).chr(0xBF) => 'y',   // y umlaut
263
264          chr(0xc4).chr(0x80) => 'a', 
265          chr(0xc4).chr(0x81) => 'a', 
266          chr(0xc4).chr(0x82) => 'a', 
267          chr(0xc4).chr(0x83) => 'a', 
268          chr(0xc4).chr(0x84) => 'a', 
269          chr(0xc4).chr(0x85) => 'a', 
270          chr(0xc4).chr(0x86) => 'c', 
271          chr(0xc4).chr(0x87) => 'c', 
272          chr(0xc4).chr(0x88) => 'c', 
273          chr(0xc4).chr(0x89) => 'c', 
274          chr(0xc4).chr(0x8A) => 'c', 
275          chr(0xc4).chr(0x8B) => 'c', 
276          chr(0xc4).chr(0x8C) => 'c', 
277          chr(0xc4).chr(0x8D) => 'c', 
278          chr(0xc4).chr(0x8E) => 'd', 
279          chr(0xc4).chr(0x8F) => 'd', 
280
281          chr(0xc4).chr(0x90) => 'd', 
282          chr(0xc4).chr(0x91) => 'd', 
283          chr(0xc4).chr(0x92) => 'e', 
284          chr(0xc4).chr(0x93) => 'e', 
285          chr(0xc4).chr(0x94) => 'e', 
286          chr(0xc4).chr(0x95) => 'e', 
287          chr(0xc4).chr(0x96) => 'e', 
288          chr(0xc4).chr(0x97) => 'e',   
289          chr(0xc4).chr(0x98) => 'e', 
290          chr(0xc4).chr(0x99) => 'e', 
291          chr(0xc4).chr(0x9A) => 'e', 
292          chr(0xc4).chr(0x9B) => 'e', 
293          chr(0xc4).chr(0x9C) => 'g', 
294          chr(0xc4).chr(0x9D) => 'g', 
295          chr(0xc4).chr(0x9E) => 'g', 
296          chr(0xc4).chr(0x9F) => 'g',   
297
298          chr(0xc4).chr(0xA0) => 'g', 
299          chr(0xc4).chr(0xA1) => 'g', 
300          chr(0xc4).chr(0xA2) => 'g', 
301          chr(0xc4).chr(0xA3) => 'g', 
302          chr(0xc4).chr(0xA4) => 'h', 
303          chr(0xc4).chr(0xA5) => 'h', 
304          chr(0xc4).chr(0xA6) => 'h', 
305          chr(0xc4).chr(0xA7) => 'h',   
306          chr(0xc4).chr(0xA8) => 'i', 
307          chr(0xc4).chr(0xA9) => 'i', 
308          chr(0xc4).chr(0xAA) => 'i', 
309          chr(0xc4).chr(0xAB) => 'i', 
310          chr(0xc4).chr(0xAC) => 'i', 
311          chr(0xc4).chr(0xAD) => 'i', 
312          chr(0xc4).chr(0xAE) => 'i', 
313          chr(0xc4).chr(0xAF) => 'i',   
314
315          chr(0xc4).chr(0xB0) => 'i', 
316          chr(0xc4).chr(0xB1) => 'i', 
317          chr(0xc4).chr(0xB2) => 'ij', 
318          chr(0xc4).chr(0xB3) => 'ij', 
319          chr(0xc4).chr(0xB4) => 'j', 
320          chr(0xc4).chr(0xB5) => 'j', 
321          chr(0xc4).chr(0xB6) => 'k', 
322          chr(0xc4).chr(0xB7) => 'k',   
323          // chr(0xc4).chr(0xB8) => '',  kra
324          chr(0xc4).chr(0xB9) => 'l', 
325          chr(0xc4).chr(0xBA) => 'l', 
326          chr(0xc4).chr(0xBB) => 'l', 
327          chr(0xc4).chr(0xBC) => 'l', 
328          chr(0xc4).chr(0xBD) => 'l', 
329          chr(0xc4).chr(0xBE) => 'l', 
330          chr(0xc4).chr(0xBF) => 'l',   
331
332          chr(0xc5).chr(0x80) => 'l',
333          chr(0xc5).chr(0x81) => 'l', 
334          chr(0xc5).chr(0x82) => 'l', 
335          chr(0xc5).chr(0x83) => 'n', 
336          chr(0xc5).chr(0x84) => 'n', 
337          chr(0xc5).chr(0x85) => 'n', 
338          chr(0xc5).chr(0x86) => 'n', 
339          chr(0xc5).chr(0x87) => 'n', 
340          chr(0xc5).chr(0x88) => 'n', 
341          chr(0xc5).chr(0x89) => 'n', 
342          chr(0xc5).chr(0x8A) => 'n',  // eng
343          chr(0xc5).chr(0x8B) => 'n',  // eng
344          chr(0xc5).chr(0x8C) => 'o', 
345          chr(0xc5).chr(0x8D) => 'o', 
346          chr(0xc5).chr(0x8E) => 'o', 
347          chr(0xc5).chr(0x8F) => 'o', 
348
349          chr(0xc5).chr(0x90) => 'o', 
350          chr(0xc5).chr(0x91) => 'o', 
351          chr(0xc5).chr(0x92) => 'oe', 
352          chr(0xc5).chr(0x93) => 'oe', 
353          chr(0xc5).chr(0x94) => 'r', 
354          chr(0xc5).chr(0x95) => 'r', 
355          chr(0xc5).chr(0x96) => 'r', 
356          chr(0xc5).chr(0x97) => 'r',   
357          chr(0xc5).chr(0x98) => 'r', 
358          chr(0xc5).chr(0x99) => 'r', 
359          chr(0xc5).chr(0x9A) => 's', 
360          chr(0xc5).chr(0x9B) => 's', 
361          chr(0xc5).chr(0x9C) => 's', 
362          chr(0xc5).chr(0x9D) => 's', 
363          chr(0xc5).chr(0x9E) => 's', 
364          chr(0xc5).chr(0x9F) => 's',   
365
366          chr(0xc5).chr(0xA0) => 's', 
367          chr(0xc5).chr(0xA1) => 's', 
368          chr(0xc5).chr(0xA2) => 't', 
369          chr(0xc5).chr(0xA3) => 't', 
370          chr(0xc5).chr(0xA4) => 't', 
371          chr(0xc5).chr(0xA5) => 't', 
372          chr(0xc5).chr(0xA6) => 't', 
373          chr(0xc5).chr(0xA7) => 't',   
374          chr(0xc5).chr(0xA8) => 'u', 
375          chr(0xc5).chr(0xA9) => 'u', 
376          chr(0xc5).chr(0xAA) => 'u', 
377          chr(0xc5).chr(0xAB) => 'u', 
378          chr(0xc5).chr(0xAC) => 'u', 
379          chr(0xc5).chr(0xAD) => 'u', 
380          chr(0xc5).chr(0xAE) => 'u', 
381          chr(0xc5).chr(0xAF) => 'u',
382
383          chr(0xc5).chr(0xB0) => 'u', 
384          chr(0xc5).chr(0xB1) => 'u', 
385          chr(0xc5).chr(0xB2) => 'u', 
386          chr(0xc5).chr(0xB3) => 'u', 
387          chr(0xc5).chr(0xB4) => 'w', 
388          chr(0xc5).chr(0xB5) => 'w', 
389          chr(0xc5).chr(0xB6) => 'y', 
390          chr(0xc5).chr(0xB7) => 'y',   
391          chr(0xc5).chr(0xB8) => 'y',
392          chr(0xc5).chr(0xB9) => 'z', 
393          chr(0xc5).chr(0xBA) => 'z', 
394          chr(0xc5).chr(0xBB) => 'z', 
395          chr(0xc5).chr(0xBC) => 'z', 
396          chr(0xc5).chr(0xBD) => 'z', 
397          chr(0xc5).chr(0xBE) => 'z', 
398          chr(0xc5).chr(0xBF) => 's',   
399   
400          chr(0xc6).chr(0x80) => 'b',
401          chr(0xc6).chr(0x81) => 'b', 
402          chr(0xc6).chr(0x82) => 'b', 
403          chr(0xc6).chr(0x83) => 'b', 
404          //chr(0xc6).chr(0x84) => '', 
405          //chr(0xc6).chr(0x85) => '',
406          //chr(0xc6).chr(0x86) => '',
407          chr(0xc6).chr(0x87) => 'c', 
408          chr(0xc6).chr(0x88) => 'c', 
409          chr(0xc6).chr(0x89) => 'd', 
410          chr(0xc6).chr(0x8A) => 'd', 
411          chr(0xc6).chr(0x8B) => 'd', 
412          chr(0xc6).chr(0x8C) => 'd', 
413          //chr(0xc6).chr(0x8D) => '', 
414          //chr(0xc6).chr(0x8E) => '', 
415          //chr(0xc6).chr(0x8F) => '', 
416
417          chr(0xc6).chr(0x90) => 'e', 
418          chr(0xc6).chr(0x91) => 'f', 
419          chr(0xc6).chr(0x92) => 'f', 
420          chr(0xc6).chr(0x93) => 'g', 
421          //chr(0xc6).chr(0x94) => '', 
422          //chr(0xc6).chr(0x95) => '', 
423          chr(0xc6).chr(0x96) => 'i', 
424          chr(0xc6).chr(0x97) => 'i',   
425          chr(0xc6).chr(0x98) => 'k', 
426          chr(0xc6).chr(0x99) => 'k', 
427          chr(0xc6).chr(0x9A) => 'l', 
428          //chr(0xc6).chr(0x9B) => 'e', 
429          //chr(0xc6).chr(0x9C) => 'g', 
430          chr(0xc6).chr(0x9D) => 'n', 
431          chr(0xc6).chr(0x9E) => 'n', 
432          chr(0xc6).chr(0x9F) => 'o',   
433
434          chr(0xc6).chr(0xA0) => 'o', 
435          chr(0xc6).chr(0xA1) => 'o', 
436          chr(0xc6).chr(0xA2) => 'oi', 
437          chr(0xc6).chr(0xA3) => 'oi', 
438          chr(0xc6).chr(0xA4) => 'p', 
439          chr(0xc6).chr(0xA5) => 'p', 
440          chr(0xc6).chr(0xA6) => 'yr', 
441          //chr(0xc6).chr(0xA7) => '',   
442          //chr(0xc6).chr(0xA8) => '', 
443          //chr(0xc6).chr(0xA9) => 'i', 
444          //chr(0xc6).chr(0xAA) => 'i', 
445          chr(0xc6).chr(0xAB) => 't', 
446          chr(0xc6).chr(0xAC) => 't', 
447          chr(0xc6).chr(0xAD) => 't', 
448          chr(0xc6).chr(0xAE) => 't', 
449          chr(0xc6).chr(0xAF) => 'u',   
450
451          chr(0xc6).chr(0xB0) => 'u', 
452          //chr(0xc6).chr(0xB1) => '', 
453          chr(0xc6).chr(0xB2) => 'v', 
454          chr(0xc6).chr(0xB3) => 'y', 
455          chr(0xc6).chr(0xB4) => 'y', 
456          chr(0xc6).chr(0xB5) => 'z', 
457          chr(0xc6).chr(0xB6) => 'z', 
458          chr(0xc6).chr(0xB7) => 'k',   
459
460          chr(0xc7).chr(0x84) => 'dz', 
461          chr(0xc7).chr(0x85) => 'dz', 
462          chr(0xc7).chr(0x86) => 'dz', 
463          chr(0xc7).chr(0x87) => 'lj', 
464          chr(0xc7).chr(0x88) => 'lj', 
465          chr(0xc7).chr(0x89) => 'lj', 
466          chr(0xc7).chr(0x8A) => 'nj',
467          chr(0xc7).chr(0x8B) => 'nj',
468          chr(0xc7).chr(0x8C) => 'nj', 
469          chr(0xc7).chr(0x8D) => 'a', 
470          chr(0xc7).chr(0x8E) => 'a', 
471          chr(0xc7).chr(0x8F) => 'i', 
472
473          chr(0xc7).chr(0x90) => 'i', 
474          chr(0xc7).chr(0x91) => 'o', 
475          chr(0xc7).chr(0x92) => 'o', 
476          chr(0xc7).chr(0x93) => 'u', 
477          chr(0xc7).chr(0x94) => 'u', 
478          chr(0xc7).chr(0x95) => 'u', 
479          chr(0xc7).chr(0x96) => 'u', 
480          chr(0xc7).chr(0x97) => 'u',   
481          chr(0xc7).chr(0x98) => 'u', 
482          chr(0xc7).chr(0x99) => 'u', 
483          chr(0xc7).chr(0x9A) => 'u', 
484          chr(0xc7).chr(0x9B) => 'u', 
485          chr(0xc7).chr(0x9C) => 'u', 
486          // chr(0xc7).chr(0x9D) => '', 
487          chr(0xc7).chr(0x9E) => 'a', 
488          chr(0xc7).chr(0x9F) => 'a',   
489
490          chr(0xc7).chr(0xA0) => 'a', 
491          chr(0xc7).chr(0xA1) => 'a', 
492          chr(0xc7).chr(0xA2) => 'ae', 
493          chr(0xc7).chr(0xA3) => 'ae', 
494          chr(0xc7).chr(0xA4) => 'g', 
495          chr(0xc7).chr(0xA5) => 'g', 
496          chr(0xc7).chr(0xA6) => 'g', 
497          chr(0xc7).chr(0xA7) => 'g',   
498          chr(0xc7).chr(0xA8) => 'k', 
499          chr(0xc7).chr(0xA9) => 'k', 
500          chr(0xc7).chr(0xAA) => 'q', 
501          chr(0xc7).chr(0xAB) => 'q', 
502          chr(0xc7).chr(0xAC) => 'q', 
503          chr(0xc7).chr(0xAD) => 'q', 
504
505          chr(0xc7).chr(0xB0) => 'j', 
506          chr(0xc7).chr(0xB1) => 'dz', 
507          chr(0xc7).chr(0xB2) => 'dz', 
508          chr(0xc7).chr(0xB3) => 'dz', 
509          chr(0xc7).chr(0xB4) => 'g', 
510          chr(0xc7).chr(0xB5) => 'g', 
511          chr(0xc7).chr(0xB8) => 'n',
512          chr(0xc7).chr(0xB9) => 'n', 
513          chr(0xc7).chr(0xBA) => 'a', 
514          chr(0xc7).chr(0xBB) => 'a', 
515          chr(0xc7).chr(0xBC) => 'ae', 
516          chr(0xc7).chr(0xBD) => 'ae', 
517          chr(0xc7).chr(0xBE) => 'o', 
518          chr(0xc7).chr(0xBF) => 'o',   
519   
520          chr(0xc8).chr(0x80) => 'a',
521          chr(0xc8).chr(0x81) => 'a', 
522          chr(0xc8).chr(0x82) => 'a', 
523          chr(0xc8).chr(0x83) => 'a', 
524          chr(0xc8).chr(0x84) => 'e', 
525          chr(0xc8).chr(0x85) => 'e', 
526          chr(0xc8).chr(0x86) => 'e', 
527          chr(0xc8).chr(0x87) => 'e', 
528          chr(0xc8).chr(0x88) => 'i', 
529          chr(0xc8).chr(0x89) => 'i', 
530          chr(0xc8).chr(0x8A) => 'i', 
531          chr(0xc8).chr(0x8B) => 'i', 
532          chr(0xc8).chr(0x8C) => 'o', 
533          chr(0xc8).chr(0x8D) => 'o', 
534          chr(0xc8).chr(0x8E) => 'o', 
535          chr(0xc8).chr(0x8F) => 'o', 
536
537          chr(0xc8).chr(0x90) => 'r', 
538          chr(0xc8).chr(0x91) => 'r', 
539          chr(0xc8).chr(0x92) => 'r', 
540          chr(0xc8).chr(0x93) => 'r', 
541          chr(0xc8).chr(0x94) => 'u', 
542          chr(0xc8).chr(0x95) => 'u', 
543          chr(0xc8).chr(0x96) => 'u', 
544          chr(0xc8).chr(0x97) => 'u',   
545          chr(0xc8).chr(0x98) => 's', 
546          chr(0xc8).chr(0x99) => 's', 
547          chr(0xc8).chr(0x9A) => 't', 
548          chr(0xc8).chr(0x9B) => 't', 
549          //chr(0xc8).chr(0x9C) => '', 
550          //chr(0xc8).chr(0x9D) => '', 
551          chr(0xc8).chr(0x9E) => 'h', 
552          chr(0xc8).chr(0x9F) => 'h',   
553
554          chr(0xc8).chr(0xA0) => 'n', 
555          chr(0xc8).chr(0xA1) => 'd', 
556          chr(0xc8).chr(0xA2) => 'ou', 
557          chr(0xc8).chr(0xA3) => 'ou', 
558          chr(0xc8).chr(0xA4) => 'z', 
559          chr(0xc8).chr(0xA5) => 'z', 
560          chr(0xc8).chr(0xA6) => 'a', 
561          chr(0xc8).chr(0xA7) => 'a',   
562          chr(0xc8).chr(0xA8) => 'e', 
563          chr(0xc8).chr(0xA9) => 'e', 
564          chr(0xc8).chr(0xAA) => 'o', 
565          chr(0xc8).chr(0xAB) => 'o', 
566          chr(0xc8).chr(0xAC) => 'o', 
567          chr(0xc8).chr(0xAD) => 'o', 
568          chr(0xc8).chr(0xAE) => 'o', 
569          chr(0xc8).chr(0xAF) => 'o',   
570
571          chr(0xc8).chr(0xB0) => 'o', 
572          chr(0xc8).chr(0xB1) => 'o', 
573          chr(0xc8).chr(0xB2) => 'y', 
574          chr(0xc8).chr(0xB3) => 'y', 
575          chr(0xc8).chr(0xB4) => 'l', 
576          chr(0xc8).chr(0xB5) => 'n', 
577          chr(0xc8).chr(0xB6) => 't', 
578          chr(0xc8).chr(0xB7) => 'j',   
579          chr(0xc8).chr(0xB8) => 'db',
580          chr(0xc8).chr(0xB9) => 'qp', 
581          chr(0xc8).chr(0xBA) => 'a', 
582          chr(0xc8).chr(0xBB) => 'c', 
583          chr(0xc8).chr(0xBC) => 'c', 
584          chr(0xc8).chr(0xBD) => 'l', 
585          chr(0xc8).chr(0xBE) => 't', 
586          chr(0xc8).chr(0xBF) => 's',   
587
588          chr(0xc9).chr(0x80) => 'z', 
589
590          chr(0xc9).chr(0x93) => 'b', 
591          chr(0xc9).chr(0x95) => 'c', 
592          chr(0xc9).chr(0x96) => 'd', 
593          chr(0xc9).chr(0x97) => 'd',   
594          chr(0xc9).chr(0x9B) => 'e', 
595
596          chr(0xc9).chr(0xA0) => 'g', 
597          chr(0xc9).chr(0xA1) => 'g', 
598          chr(0xc9).chr(0xA2) => 'g', 
599          chr(0xc9).chr(0xA6) => 'h', 
600          chr(0xc9).chr(0xA7) => 'h',   
601          chr(0xc9).chr(0xA8) => 'i', 
602          chr(0xc9).chr(0xA9) => 'i', 
603          chr(0xc9).chr(0xAA) => 'i', 
604          chr(0xc9).chr(0xAB) => 'l', 
605          chr(0xc9).chr(0xAC) => 'l', 
606          chr(0xc9).chr(0xAD) => 'l', 
607
608          chr(0xc9).chr(0xB1) => 'm', 
609          chr(0xc9).chr(0xB2) => 'n', 
610          chr(0xc9).chr(0xB3) => 'n', 
611          chr(0xc9).chr(0xB4) => 'n', 
612          chr(0xc9).chr(0xB5) => 'o', 
613          chr(0xc9).chr(0xB6) => 'oe',
614          chr(0xc9).chr(0xB9) => 'r', 
615          chr(0xc9).chr(0xBA) => 'r', 
616          chr(0xc9).chr(0xBB) => 'r', 
617          chr(0xc9).chr(0xBC) => 'r', 
618          chr(0xc9).chr(0xBD) => 'r', 
619          chr(0xc9).chr(0xBE) => 'r', 
620          chr(0xc9).chr(0xBF) => 'r'   
621   
622          // possibly others from the extended latin sets
623        ),
624        // alternate replacements:
625        array(
626          chr(0xc3).chr(0x84) => 'ae',  // A umlaut
627          chr(0xc3).chr(0xA4) => 'ae',  // a umlaut
628
629          chr(0xc3).chr(0x96) => 'oe',  // O umlaut
630          chr(0xc3).chr(0xB6) => 'oe',  // o umlaut
631
632          chr(0xc3).chr(0x9C) => 'ue',  // U umlaut
633          chr(0xc3).chr(0xBC) => 'ue',  // u umlaut
634
635          chr(0xc3).chr(0x85) => 'a', // A ring
636          chr(0xc3).chr(0xA5) => 'a', // a ring
637
638          chr(0xc3).chr(0x98) => 'oe',  // O slash (scandinavian)
639          chr(0xc3).chr(0xB8) => 'oe',  // o slash
640
641          chr(0xc3).chr(0xB1) => 'ng',  // n tilde, spanish
642
643          chr(0xc3).chr(0x90) => 'dh',  // D bar (eth)
644          chr(0xc3).chr(0xB0) => 'dh',  // lower case eth
645        ),
646        array(
647          chr(0xc3).chr(0xB1) => 'ny',  // n tilde, catalan
648        ),
649        array(
650          chr(0xc3).chr(0xB1) => 'nh',  // n tilde, portugese
651        ),
652      );
653    }
654
655    /* separate the search terms into words */
656    $terms = explode(' ', $term);
657    $canon = '';
658    $prefix = '';
659    $usedreplacement = TRUE;
660
661    /* try each replacements table */
662    for($alt = 0; $alt < count($replacements); $alt++) {
663      $replacement =& $replacements[$alt];
664      $thiscanon = '';
665      foreach ($terms as $term) {
666        /* remove apostrophe-s: these are always stored and searched in the singular
667           non-possessive so that (the church of, for example) 'St Andrew's',
668           'St Andrews' and 'St Andrew' all match equivalently */
669        $term = preg_replace('~\\\'s$~', '', trim(strtolower($term)));
670        if (empty($term)) { continue; }
671
672        $l = mb_strlen($term, 'UTF-8');
673        $s = '';
674        for ($i = 0; $i < $l; $i++) {
675          /* replace listed UTF-8 characters with their ascii
676             equivalents.  For search terms we only replace from the
677             main replacement table, but so that we get alternatives
678             to search for, we replace from all the tables in turn
679             (falling back to the main table if not in the alternates */
680          $c = mb_substr($term, $i, 1, 'UTF-8');
681          if (array_key_exists($c, $replacement)) {
682            $s .= $replacement[$c];
683            $usedreplacement = TRUE;
684          } else if ($alt > 0 && array_key_exists($c, $replacements[0])) {
685            $s .= $replacements[0][$c];
686          } else {
687            $s .= $c;
688          }
689        }
690        $s = trim($s);
691        if (empty($s)) { continue; }
692
693        $thiscanon .= ";{$s};";  /* see above re note about semicolon delimiters */
694        while (strpos($thiscanon, ';;;') !== FALSE) {
695          $thiscanon = str_replace(';;;', ';;', $thiscanon); 
696          /* ... arising from replacing multiple consecutive chars with space */
697        }
698        if (strlen($thiscanon) > 2 && substr($thiscanon, strlen($thiscanon)-2, 2) == ';;') {
699          $thiscanon = substr($thiscanon, 0, strlen($thiscanon)-1);
700        }
701      }
702      if ($usedreplacement) {
703        $canon .= $prefix . $thiscanon;
704        $prefix = '#'; /* see above re note about the hash sign */
705      }
706      if (! $alternates) { break; }
707      $usedreplacement = FALSE;
708    }
709    return $canon;
710  }
711
712  // --------------------------------------------------
713  /* static */ function canonical_with_synonym($term) {
714    /* cononicalise the term as above, but also create mutliple
715       canonical strings where each has a variation in common
716       abbreviations (road for rd etc, and vice-versa, and singnular
717       for plural - that's particularly important for church names and
718       similar, where we want to match "St John's" with "St John" or
719       "St Johns" (simple canonicalisation will have removed the
720       apostrophe, so the plural to singular also acts as possessive
721       to non-possessive */
722
723    static $synonyms = array(
724      'road'=>'rd',          'rd'=>'road',
725      'street'=>'st',        'st'=>array('street','saint'),
726      'avenue'=>'ave',       'ave'=>'avenue',
727      'crescent'=>'cres',    'cres'=>'crescent',
728      'close'=>'cl',         'cl'=>'close',
729      'way'=>'wy',           'wy'=>'way',
730      'highway'=>'hwy',      'hwy'=>'highway',
731      'house'=>'hse',        'hse'=>'house',
732      'court'=>'ct',         'ct'=>'court',
733      'park'=>'pk',          'pk'=>'park',
734      'lane'=>'ln',          'ln'=>'lane',
735      'rue'=>'r',            'r'=>'rue',
736      'boulevard'=>'blvd',   'blvd'=>'boulevard',
737      'boulevard'=>'bvd',    'bvd'=>'boulevard',
738      'drive'=>'drv',        'drv'=>'drive',
739      'saint'=>'st',         // see above
740      'international'=>'intl', 'intl'=>'international', // as in airports
741      'stn'=>'station',      'station'=>'stn',
742      'north'=>'n',          'n'=>'north',
743      'south'=>'s',          's'=>'south',
744      'east'=>'e',           'e'=>'east',
745      'west'=>'w',           'w'=>'west'
746    );
747
748    $term = canon::canonical($term);
749    if ($term == '') { return array(); }
750
751    $words = explode(';', $term); // expect blanks at start and end
752    $terms = array('');
753    for ($w = 1; $w < count($words); $w++) {
754      $word = $words[$w];
755      $c = count($terms);
756      if (! empty($synonyms[$word])) {
757        $syns = is_array($synonyms[$word]) ? $synonyms[$word] : array($synonyms[$word]);
758        for ($j = 0; $j < count($syns); $j++) {
759          for ($i = 0; $i < $c; $i++) { $terms[$i+($j+1)*$c] = $terms[$i].';'.$syns[$j]; }
760        }
761      } else {
762        $lastchar = strlen($word) - 1;
763        if ($lastchar >= 0 && $word{$lastchar} == 's') {
764          /* apply singular form too, only for 's' not 'es' or other peculiarities */
765          for ($i = 0; $i < $c; $i++) { $terms[$i+$c] = $terms[$i].';'.substr($word,0,$lastchar); }
766        }
767      }
768      for ($i = 0; $i < $c; $i++) { $terms[$i] .= ';'.$word; }
769    }
770
771    return $terms;
772  }
773
774  // --------------------------------------------------
775  /* static */ function likecanon1($name, $exact=FALSE) {
776    /* generates a SQL fragment which compares canonical indexes with
777    given (canonical) name.  exact is a boolean which will mean the
778    match has to be exactly word for word (though each word may still
779    have accented variants); when false the index need only contain
780    all the words in 'name' in the same order to match, though there may be other
781    words before, after or in between. For example, 'Hinton Road' canonicalises
782    to ';hinton;road;'. We may have '#;hinton;road;#'
783    and say '#;cherry;hinton;road;#' in the index. Exact match catches only the first,
784    non-exact both */
785
786    $wild = $exact ? '' : '%';
787    return y_op::like('canon', '%#'.$wild.str_replace(';;', ";{$wild};", $name).$wild.'#%');
788  }
789
790  // --------------------------------------------------
791  /* static */ function likecanon($names, $exact=FALSE) {
792    /* generates SQL fragment which ors each of the matches for names from likecanon1  */
793    if (count($names) == 1) { return canon::likecanon1($names[0], $exact); }
794    $ors = array();
795    foreach ($names as $name) { $ors[] = canon::likecanon1($name, $exact); }
796    return y_op::oor($ors);
797  }
798
799  // --------------------------------------------------
800  /* static */ function distancerestriction($lat, $lon) {
801    /* This generates a SQL fragment for ORDER BY so that names come back sorted by distance
802       from given latitude and longitude */
803    return y_op::oprintf("(pow(%f - {$lat},2) + pow(%f - {$lon},2))", 'lat', 'lon');
804  }
805
806
807  // --------------------------------------------------
808  /* static */ function getuniqueid($osmid, $type) {
809    /* osm ids are only unique within type (node, segement, way), so we make them unique
810       overall by inserting in the osm id an extra loworder decimal digit for the type */
811    static $types;
812    if (! isset($types)) { $types = array_flip(canon::getosmtypes()); }
813    return 10 *$osmid + $types[$type];
814  }
815
816  // --------------------------------------------------
817  /* static */ function getosmid($id, &$type) {
818    /* converts from name finder id to osm id; the converse of getuniqueid above */
819    static $types;
820    if (! isset($types)) { $types = canon::getosmtypes(); }
821    $typeindex = $id % 10;
822    $type = $types[$typeindex];
823    return (int)floor(($id/10));
824  }
825
826  // --------------------------------------------------
827  /* static */ function getosmtypes() {
828    static $types = array(1=>'node',2=>'segment',3=>'way');   
829    return $types;
830  }
831 
832}
833
834?>
Note: See TracBrowser for help on using the repository browser.