| 1112 | |
| 1113 | template <typename Fn> |
| 1114 | void Geocoder::WithPostcodes(BaseContext & ctx, Fn && fn) |
| 1115 | { |
| 1116 | TRACE(WithPostcodes); |
| 1117 | |
| 1118 | size_t const maxPostcodeTokens = GetMaxNumTokensInPostcode(); |
| 1119 | auto worldContext = GetWorldContext(m_dataSource); |
| 1120 | |
| 1121 | size_t const numTokens = ctx.NumTokens(); |
| 1122 | for (size_t startToken = 0; startToken != numTokens; ++startToken) |
| 1123 | { |
| 1124 | size_t endToken = startToken; |
| 1125 | for (size_t n = 1; startToken + n <= numTokens && n <= maxPostcodeTokens; ++n) |
| 1126 | { |
| 1127 | if (ctx.IsTokenUsed(startToken + n - 1)) |
| 1128 | break; |
| 1129 | |
| 1130 | TokenSlice slice(m_params, TokenRange(startToken, startToken + n)); |
| 1131 | auto const isPrefix = startToken + n == numTokens; |
| 1132 | if (LooksLikePostcode(QuerySlice(slice), isPrefix)) |
| 1133 | endToken = startToken + n; |
| 1134 | } |
| 1135 | if (startToken == endToken) |
| 1136 | continue; |
| 1137 | |
| 1138 | TokenRange const tokenRange(startToken, endToken); |
| 1139 | |
| 1140 | // Find features by exact postcode match from OSM data. |
| 1141 | auto postcodes = RetrievePostcodeFeatures(*m_context, TokenSlice(m_params, tokenRange)); |
| 1142 | |
| 1143 | // Get _around postcode_ features from additional _external postcodes_ section. |
| 1144 | // Do not emit _around postcode_ only features if we don't have some other tokens to match (avoid lags). |
| 1145 | if (tokenRange.Size() < numTokens && m_context->m_value.m_cont.IsExist(POSTCODE_POINTS_FILE_TAG)) |
| 1146 | { |
| 1147 | auto & postcodePoints = m_postcodePointsCache.Get(*m_context); |
| 1148 | UniString postcodeQuery; |
| 1149 | JoinQueryTokens(m_params, tokenRange, UniString::kSpace /* sep */, postcodeQuery); |
| 1150 | vector<m2::PointD> points; |
| 1151 | postcodePoints.Get(postcodeQuery, points); |
| 1152 | |
| 1153 | // PostcodePoints::Get returns many points for "BN1" or one point for "BN1 3LJ". |
| 1154 | // Do aggregate rects for a _wide_ postcode prefix match. |
| 1155 | m2::RectD rect; |
| 1156 | for (auto const & p : points) |
| 1157 | rect.Add(mercator::RectByCenterXYAndOffset(p, postcodePoints.GetRadius())); |
| 1158 | |
| 1159 | postcodes = postcodes.Union(RetrieveGeometryFeatures(*m_context, rect, RectId::Postcode)); |
| 1160 | } |
| 1161 | |
| 1162 | SCOPE_GUARD(cleanup, [&]() { m_postcodes.Clear(); }); |
| 1163 | |
| 1164 | if (!postcodes.IsEmpty()) |
| 1165 | { |
| 1166 | ScopedMarkTokens mark(ctx.m_tokens, BaseContext::TOKEN_TYPE_POSTCODE, tokenRange); |
| 1167 | |
| 1168 | m_postcodes.Clear(); |
| 1169 | |
| 1170 | if (worldContext) |
| 1171 | m_postcodes.m_worldFeatures = RetrievePostcodeFeatures(*worldContext, TokenSlice(m_params, tokenRange)); |
nothing calls this directly
no test coverage detected