]> git.openstreetmap.org Git - nominatim.git/blob - sql/functions/ranking.sql
add explicit bbox contains check
[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 --
121 -- \param country        Two-letter country code where the object is in.
122 -- \param extended_type  OSM type (N, W, R) or area type (A).
123 -- \param place_class    Class (or tag key) of object.
124 -- \param place_type     Type (or tag value) of object.
125 -- \param admin_level    Value of admin_level tag.
126 -- \param is_major       If true, boost search rank by one.
127 -- \param postcode       Value of addr:postcode tag.
128 -- \param[out] search_rank   Computed search rank.
129 -- \param[out] address_rank  Computed address rank.
130 --
131 CREATE OR REPLACE FUNCTION compute_place_rank(country VARCHAR(2),
132                                               extended_type VARCHAR(1),
133                                               place_class TEXT, place_type TEXT,
134                                               admin_level SMALLINT,
135                                               is_major BOOLEAN,
136                                               postcode TEXT,
137                                               OUT search_rank SMALLINT,
138                                               OUT address_rank SMALLINT)
139 AS $$
140 DECLARE
141   classtype TEXT;
142 BEGIN
143   IF place_class in ('place','boundary')
144      and place_type in ('postcode','postal_code')
145   THEN
146     SELECT * INTO search_rank, address_rank
147       FROM get_postcode_rank(country, postcode);
148   ELSEIF extended_type = 'N' AND place_class = 'highway' THEN
149     search_rank = 30;
150     address_rank = 0;
151   ELSEIF place_class = 'landuse' AND extended_type != 'A' THEN
152     search_rank = 30;
153     address_rank = 0;
154   ELSE
155     IF place_class = 'boundary' and place_type = 'administrative' THEN
156       classtype = place_type || admin_level::TEXT;
157     ELSE
158       classtype = place_type;
159     END IF;
160
161     SELECT l.rank_search, l.rank_address INTO search_rank, address_rank
162       FROM address_levels l
163      WHERE (l.country_code = country or l.country_code is NULL)
164            AND l.class = place_class AND (l.type = classtype or l.type is NULL)
165      ORDER BY l.country_code, l.class, l.type LIMIT 1;
166
167     IF search_rank is NULL THEN
168       search_rank := 30;
169     END IF;
170
171     IF address_rank is NULL THEN
172       address_rank := 30;
173     END IF;
174
175     -- some postcorrections
176     IF place_class = 'waterway' AND extended_type = 'R' THEN
177         -- Slightly promote waterway relations so that they are processed
178         -- before their members.
179         search_rank := search_rank - 1;
180     END IF;
181
182     IF is_major THEN
183       search_rank := search_rank - 1;
184     END IF;
185   END IF;
186 END;
187 $$
188 LANGUAGE plpgsql IMMUTABLE;