]> git.openstreetmap.org Git - nominatim.git/blob - sql/functions/ranking.sql
factor out computation of address and search rank
[nominatim.git] / sql / functions / ranking.sql
1 -- Functions related to search and address ranks
2
3 -- Return an approximate search radius according to the search rank.
4 CREATE OR REPLACE FUNCTION reverse_place_diameter(rank_search SMALLINT)
5   RETURNS FLOAT
6   AS $$
7 BEGIN
8   IF rank_search <= 4 THEN
9     RETURN 5.0;
10   ELSIF rank_search <= 8 THEN
11     RETURN 1.8;
12   ELSIF rank_search <= 12 THEN
13     RETURN 0.6;
14   ELSIF rank_search <= 17 THEN
15     RETURN 0.16;
16   ELSIF rank_search <= 18 THEN
17     RETURN 0.08;
18   ELSIF rank_search <= 19 THEN
19     RETURN 0.04;
20   END IF;
21
22   RETURN 0.02;
23 END;
24 $$
25 LANGUAGE plpgsql IMMUTABLE;
26
27
28 -- Return an approximate update radius according to the search rank.
29 CREATE OR REPLACE FUNCTION update_place_diameter(rank_search SMALLINT)
30   RETURNS FLOAT
31   AS $$
32 BEGIN
33   -- postcodes
34   IF rank_search = 11 or rank_search = 5 THEN
35     RETURN 0.05;
36   -- anything higher than city is effectively ignored (polygon required)
37   ELSIF rank_search < 16 THEN
38     RETURN 0;
39   ELSIF rank_search < 18 THEN
40     RETURN 0.1;
41   ELSIF rank_search < 20 THEN
42     RETURN 0.05;
43   ELSIF rank_search = 21 THEN
44     RETURN 0.001;
45   ELSIF rank_search < 24 THEN
46     RETURN 0.02;
47   ELSIF rank_search < 26 THEN
48     RETURN 0.002;
49   ELSIF rank_search < 28 THEN
50     RETURN 0.001;
51   END IF;
52
53   RETURN 0;
54 END;
55 $$
56 LANGUAGE plpgsql IMMUTABLE;
57
58
59 -- Guess a ranking for postcodes from country and postcode format.
60 CREATE OR REPLACE FUNCTION get_postcode_rank(country_code VARCHAR(2), postcode TEXT,
61                                              OUT rank_search SMALLINT,
62                                              OUT rank_address SMALLINT)
63 AS $$
64 DECLARE
65   part TEXT;
66 BEGIN
67     rank_search := 30;
68     rank_address := 30;
69     postcode := upper(postcode);
70
71     IF country_code = 'gb' THEN
72         IF postcode ~ '^([A-Z][A-Z]?[0-9][0-9A-Z]? [0-9][A-Z][A-Z])$' THEN
73             rank_search := 25;
74             rank_address := 5;
75         ELSEIF postcode ~ '^([A-Z][A-Z]?[0-9][0-9A-Z]? [0-9])$' THEN
76             rank_search := 23;
77             rank_address := 5;
78         ELSEIF postcode ~ '^([A-Z][A-Z]?[0-9][0-9A-Z])$' THEN
79             rank_search := 21;
80             rank_address := 5;
81         END IF;
82
83     ELSEIF country_code = 'sg' THEN
84         IF postcode ~ '^([0-9]{6})$' THEN
85             rank_search := 25;
86             rank_address := 11;
87         END IF;
88
89     ELSEIF country_code = 'de' THEN
90         IF postcode ~ '^([0-9]{5})$' THEN
91             rank_search := 21;
92             rank_address := 11;
93         END IF;
94
95     ELSE
96         -- Guess at the postcode format and coverage (!)
97         IF postcode ~ '^[A-Z0-9]{1,5}$' THEN -- Probably too short to be very local
98             rank_search := 21;
99             rank_address := 11;
100         ELSE
101             -- Does it look splitable into and area and local code?
102             part := substring(postcode from '^([- :A-Z0-9]+)([- :][A-Z0-9]+)$');
103
104             IF part IS NOT NULL THEN
105                 rank_search := 25;
106                 rank_address := 11;
107             ELSEIF postcode ~ '^[- :A-Z0-9]{6,}$' THEN
108                 rank_search := 21;
109                 rank_address := 11;
110             END IF;
111         END IF;
112     END IF;
113
114 END;
115 $$
116 LANGUAGE plpgsql IMMUTABLE;
117
118
119 -- Get standard search and address rank for an object
120 CREATE OR REPLACE FUNCTION compute_place_rank(country VARCHAR(2),
121                                               osm_type VARCHAR(1),
122                                               place_class TEXT, place_type TEXT,
123                                               admin_level SMALLINT,
124                                               is_area BOOLEAN, is_major BOOLEAN,
125                                               postcode TEXT,
126                                               OUT search_rank SMALLINT,
127                                               OUT address_rank SMALLINT)
128 AS $$
129 DECLARE
130   classtype TEXT;
131 BEGIN
132   IF place_class in ('place','boundary')
133      and place_type in ('postcode','postal_code')
134   THEN
135     SELECT * INTO search_rank, address_rank
136       FROM get_postcode_rank(country, postcode);
137
138     IF NOT is_area THEN
139       address_rank := 0;
140     END IF;
141   ELSEIF osm_type = 'N' AND place_class = 'highway' THEN
142     search_rank = 30;
143     address_rank = 0;
144   ELSEIF place_class = 'landuse' AND NOT is_area THEN
145     search_rank = 30;
146     address_rank = 0;
147   ELSE
148     IF place_class = 'boundary' and place_type = 'administrative' THEN
149       classtype = place_type || admin_level::TEXT;
150     ELSE
151       classtype = place_type;
152     END IF;
153
154     SELECT l.rank_search, l.rank_address INTO search_rank, address_rank
155       FROM address_levels l
156      WHERE (l.country_code = country or l.country_code is NULL)
157            AND l.class = place_class AND (l.type = classtype or l.type is NULL)
158      ORDER BY l.country_code, l.class, l.type LIMIT 1;
159
160     IF search_rank is NULL THEN
161       search_rank := 30;
162     END IF;
163
164     IF address_rank is NULL THEN
165       address_rank := 30;
166     END IF;
167
168     -- some postcorrections
169     IF place_class = 'waterway' AND osm_type = 'R' THEN
170         -- Slightly promote waterway relations so that they are processed
171         -- before their members.
172         search_rank := search_rank - 1;
173     END IF;
174
175     IF is_major THEN
176       search_rank := search_rank - 1;
177     END IF;
178   END IF;
179 END;
180 $$
181 LANGUAGE plpgsql IMMUTABLE;