| 172 | } |
| 173 | |
| 174 | std::vector<FeatureBuilder> PlaceProcessor::ProcessPlaces(std::vector<IDsContainerT> * ids /* = nullptr*/) |
| 175 | { |
| 176 | std::vector<FeatureBuilder> finalPlaces; |
| 177 | for (auto const & name2PlacesEntry : m_nameToPlaces) |
| 178 | { |
| 179 | for (auto const & cluster : FindClusters(name2PlacesEntry.second)) |
| 180 | { |
| 181 | auto best = std::max_element(cluster.begin(), cluster.end(), |
| 182 | [](FeaturePlace const * l, FeaturePlace const * r) { return IsWorsePlace(*l, *r); }); |
| 183 | |
| 184 | auto bestFb = (*best)->m_fb; |
| 185 | |
| 186 | /// @todo Assign FB point from possible boundary node from |bestBnd| below? |
| 187 | if (bestFb.IsArea()) |
| 188 | TransformToPoint(bestFb); |
| 189 | |
| 190 | IDsContainerT fbIDs; |
| 191 | fbIDs.reserve(cluster.size()); |
| 192 | base::Transform(cluster, std::back_inserter(fbIDs), |
| 193 | [](FeaturePlace const * fp) { return fp->m_fb.GetMostGenericOsmId(); }); |
| 194 | |
| 195 | auto const bestBnd = m_boundariesHolder.GetBestBoundary(fbIDs, bestFb.GetKeyPoint()); |
| 196 | if (bestBnd) |
| 197 | { |
| 198 | auto const id = bestFb.GetMostGenericOsmId(); |
| 199 | |
| 200 | // Sanity check |
| 201 | using namespace ftypes; |
| 202 | auto const bndLocality = bestBnd->GetPlace(); |
| 203 | auto const fbLocality = IsLocalityChecker::Instance().GetType((*best)->GetPlaceType()); |
| 204 | if (bndLocality != fbLocality) |
| 205 | { |
| 206 | LOG(LWARNING, (m_logTag, "Different locality types:", bndLocality, fbLocality, "for", id)); |
| 207 | // Happens in USA. For example: https://www.openstreetmap.org/relation/6603680 |
| 208 | if (bndLocality == LocalityType::Village && fbLocality == LocalityType::City) |
| 209 | continue; |
| 210 | } |
| 211 | |
| 212 | double exactArea = 0.0; |
| 213 | bool const checkArea = !bestBnd->IsHonestCity(); |
| 214 | |
| 215 | // Assign bestBoundary to the bestFb. |
| 216 | for (auto const & poly : bestBnd->m_boundary) |
| 217 | { |
| 218 | if (checkArea) |
| 219 | exactArea += generator::AreaOnEarth(poly); |
| 220 | m_boundariesTable.Append(id, indexer::CityBoundary(poly)); |
| 221 | } |
| 222 | |
| 223 | if (checkArea) |
| 224 | { |
| 225 | // Heuristic: filter *very* big Relation borders. Some examples: |
| 226 | // https://www.openstreetmap.org/relation/2374222 |
| 227 | // https://www.openstreetmap.org/relation/3388660 |
| 228 | |
| 229 | double const circleArea = |
| 230 | ms::CircleAreaOnEarth(GetRadiusByPopulationForRouting(bestBnd->GetPopulation(), bndLocality)); |
| 231 | if (exactArea > circleArea * 20.0) |