|  | @@ -9,11 +9,12 @@
 | 
	
		
			
			| 9 | 9 |  #include <string>
 | 
	
		
			
			| 10 | 10 |  #include "osmfixer/index/StatIdx.h"
 | 
	
		
			
			| 11 | 11 |  #include "util/geo/BezierCurve.h"
 | 
	
		
			
			|  | 12 | +#include "util/log/Log.h"
 | 
	
		
			
			| 12 | 13 |  
 | 
	
		
			
			| 13 |  | -using osmfixer::StatIdx;
 | 
	
		
			
			|  | 14 | +using osmfixer::Group;
 | 
	
		
			
			| 14 | 15 |  using osmfixer::OsmAttrs;
 | 
	
		
			
			|  | 16 | +using osmfixer::StatIdx;
 | 
	
		
			
			| 15 | 17 |  using osmfixer::Station;
 | 
	
		
			
			| 16 |  | -using osmfixer::Group;
 | 
	
		
			
			| 17 | 18 |  using osmfixer::Suggestion;
 | 
	
		
			
			| 18 | 19 |  
 | 
	
		
			
			| 19 | 20 |  // _____________________________________________________________________________
 | 
	
	
		
			
			|  | @@ -23,6 +24,7 @@ void StatIdx::readFromFile(const std::string& path) {
 | 
	
		
			
			| 23 | 24 |    std::string line;
 | 
	
		
			
			| 24 | 25 |  
 | 
	
		
			
			| 25 | 26 |    // first, parse nodes
 | 
	
		
			
			|  | 27 | +  LOG(INFO) << "Parsing nodes...";
 | 
	
		
			
			| 26 | 28 |    while (std::getline(f, line)) {
 | 
	
		
			
			| 27 | 29 |      // empty line is separator between blocks
 | 
	
		
			
			| 28 | 30 |      if (line.size() == 0) break;
 | 
	
	
		
			
			|  | @@ -50,8 +52,10 @@ void StatIdx::readFromFile(const std::string& path) {
 | 
	
		
			
			| 50 | 52 |  
 | 
	
		
			
			| 51 | 53 |      addStation(osmid, lat, lng, origGroup, group, attrs);
 | 
	
		
			
			| 52 | 54 |    }
 | 
	
		
			
			|  | 55 | +  LOG(INFO) << "Done.";
 | 
	
		
			
			| 53 | 56 |  
 | 
	
		
			
			| 54 | 57 |    // second, parse groups
 | 
	
		
			
			|  | 58 | +  LOG(INFO) << "Parsing groups... ";
 | 
	
		
			
			| 55 | 59 |    while (std::getline(f, line)) {
 | 
	
		
			
			| 56 | 60 |      // empty line is separator between blocks
 | 
	
		
			
			| 57 | 61 |      if (line.size() == 0) break;
 | 
	
	
		
			
			|  | @@ -65,7 +69,10 @@ void StatIdx::readFromFile(const std::string& path) {
 | 
	
		
			
			| 65 | 69 |      addGroup(osmid, attrs);
 | 
	
		
			
			| 66 | 70 |    }
 | 
	
		
			
			| 67 | 71 |  
 | 
	
		
			
			|  | 72 | +  LOG(INFO) << "Done.";
 | 
	
		
			
			|  | 73 | +
 | 
	
		
			
			| 68 | 74 |    // third, parse attr errs
 | 
	
		
			
			|  | 75 | +  LOG(INFO) << "Parsing errors... ";
 | 
	
		
			
			| 69 | 76 |    while (std::getline(f, line)) {
 | 
	
		
			
			| 70 | 77 |      // empty line is separator between blocks
 | 
	
		
			
			| 71 | 78 |      if (line.size() == 0) break;
 | 
	
	
		
			
			|  | @@ -87,10 +94,13 @@ void StatIdx::readFromFile(const std::string& path) {
 | 
	
		
			
			| 87 | 94 |  
 | 
	
		
			
			| 88 | 95 |      _stations[id].attrErrs.push_back({attr, otherAttr, conf, otherId});
 | 
	
		
			
			| 89 | 96 |    }
 | 
	
		
			
			|  | 97 | +  LOG(INFO) << "Done.";
 | 
	
		
			
			| 90 | 98 |  
 | 
	
		
			
			|  | 99 | +  LOG(INFO) << "Building indices...";
 | 
	
		
			
			| 91 | 100 |    initGroups();
 | 
	
		
			
			| 92 | 101 |    initSuggestions();
 | 
	
		
			
			| 93 | 102 |    initIndex();
 | 
	
		
			
			|  | 103 | +  LOG(INFO) << "Done.";
 | 
	
		
			
			| 94 | 104 |  }
 | 
	
		
			
			| 95 | 105 |  
 | 
	
		
			
			| 96 | 106 |  // _____________________________________________________________________________
 | 
	
	
		
			
			|  | @@ -99,7 +109,15 @@ void StatIdx::addStation(size_t osmid, double lat, double lng, size_t origGroup,
 | 
	
		
			
			| 99 | 109 |    auto point = util::geo::latLngToWebMerc<double>(lat, lng);
 | 
	
		
			
			| 100 | 110 |  
 | 
	
		
			
			| 101 | 111 |    _stations.emplace_back(
 | 
	
		
			
			| 102 |  | -      Station{_stations.size(), osmid, origGroup, group, point, attrs});
 | 
	
		
			
			|  | 112 | +      Station{_stations.size(),
 | 
	
		
			
			|  | 113 | +              osmid,
 | 
	
		
			
			|  | 114 | +              origGroup,
 | 
	
		
			
			|  | 115 | +              group,
 | 
	
		
			
			|  | 116 | +              point,
 | 
	
		
			
			|  | 117 | +              util::geo::webMercToLatLng<double>(point.getX(), point.getY()),
 | 
	
		
			
			|  | 118 | +              attrs,
 | 
	
		
			
			|  | 119 | +              {},
 | 
	
		
			
			|  | 120 | +              {}});
 | 
	
		
			
			| 103 | 121 |  
 | 
	
		
			
			| 104 | 122 |    // extend bounding box
 | 
	
		
			
			| 105 | 123 |    _bbox = util::geo::extendBox(point, _bbox);
 | 
	
	
		
			
			|  | @@ -119,19 +137,22 @@ void StatIdx::initGroups() {
 | 
	
		
			
			| 119 | 137 |    for (size_t i = 0; i < _stations.size(); i++) {
 | 
	
		
			
			| 120 | 138 |      // this should be ensured by the input file
 | 
	
		
			
			| 121 | 139 |      assert(_stations[i].origGroup < _groups.size());
 | 
	
		
			
			| 122 |  | -    _groups[_stations[i].origGroup].stations.push_back(i);
 | 
	
		
			
			|  | 140 | +    _groups[_stations[i].origGroup].polyStations.push_back(i);
 | 
	
		
			
			|  | 141 | +
 | 
	
		
			
			|  | 142 | +    _groups[_stations[i].group].stations.push_back(i);
 | 
	
		
			
			| 123 | 143 |  
 | 
	
		
			
			| 124 | 144 |      assert(_stations[i].group < _groups.size());
 | 
	
		
			
			| 125 | 145 |      if (_stations[i].group != _stations[i].origGroup &&
 | 
	
		
			
			| 126 | 146 |          _groups[_stations[i].group].osmid == 1) {
 | 
	
		
			
			| 127 |  | -      // only add non-orig groups to polygons of new (non-osm) groups
 | 
	
		
			
			| 128 |  | -      _groups[_stations[i].group].stations.push_back(i);
 | 
	
		
			
			|  | 147 | +      // only add non-orig stations to polygons of new (non-osm) groups
 | 
	
		
			
			|  | 148 | +      _groups[_stations[i].group].polyStations.push_back(i);
 | 
	
		
			
			| 129 | 149 |      }
 | 
	
		
			
			| 130 | 150 |    }
 | 
	
		
			
			| 131 | 151 |  
 | 
	
		
			
			|  | 152 | +  // build hull polygon
 | 
	
		
			
			| 132 | 153 |    for (size_t i = 0; i < _groups.size(); i++) {
 | 
	
		
			
			| 133 | 154 |      util::geo::MultiPoint<double> mp;
 | 
	
		
			
			| 134 |  | -    for (size_t stid : _groups[i].stations) {
 | 
	
		
			
			|  | 155 | +    for (size_t stid : _groups[i].polyStations) {
 | 
	
		
			
			| 135 | 156 |        double rad = 11.0;
 | 
	
		
			
			| 136 | 157 |        int n = 20;
 | 
	
		
			
			| 137 | 158 |        for (int i = 0; i < n; i++) {
 | 
	
	
		
			
			|  | @@ -150,16 +171,99 @@ void StatIdx::initGroups() {
 | 
	
		
			
			| 150 | 171 |  
 | 
	
		
			
			| 151 | 172 |  // _____________________________________________________________________________
 | 
	
		
			
			| 152 | 173 |  void StatIdx::initIndex() {
 | 
	
		
			
			| 153 |  | -  _sgrid = util::geo::Grid<size_t, util::geo::Point, double>(5000, 5000, _bbox,
 | 
	
		
			
			| 154 |  | -                                                             false);
 | 
	
		
			
			| 155 |  | -  _ggrid = util::geo::Grid<size_t, util::geo::Polygon, double>(5000, 5000,
 | 
	
		
			
			|  | 174 | +  double gSize = 10000;
 | 
	
		
			
			|  | 175 | +  _sgrid = util::geo::Grid<size_t, util::geo::Point, double>(gSize, gSize,
 | 
	
		
			
			|  | 176 | +                                                             _bbox, false);
 | 
	
		
			
			|  | 177 | +  _ggrid = util::geo::Grid<size_t, util::geo::Polygon, double>(gSize, gSize,
 | 
	
		
			
			| 156 | 178 |                                                                 _bbox, false);
 | 
	
		
			
			| 157 |  | -  _suggrid = util::geo::Grid<size_t, util::geo::Line, double>(5000, 5000, _bbox,
 | 
	
		
			
			| 158 |  | -                                                              false);
 | 
	
		
			
			|  | 179 | +  _suggrid = util::geo::Grid<size_t, util::geo::Line, double>(gSize, gSize,
 | 
	
		
			
			|  | 180 | +                                                              _bbox, false);
 | 
	
		
			
			|  | 181 | +
 | 
	
		
			
			| 159 | 182 |    for (size_t i = 0; i < _stations.size(); i++) _sgrid.add(_stations[i].pos, i);
 | 
	
		
			
			| 160 | 183 |    for (size_t i = 0; i < _groups.size(); i++) _ggrid.add(_groups[i].poly, i);
 | 
	
		
			
			| 161 | 184 |    for (size_t i = 0; i < _suggestions.size(); i++)
 | 
	
		
			
			| 162 | 185 |      _suggrid.add(_suggestions[i].arrow, i);
 | 
	
		
			
			|  | 186 | +
 | 
	
		
			
			|  | 187 | +  _heatGridsOk.resize(10);
 | 
	
		
			
			|  | 188 | +  _heatGridsSugg.resize(10);
 | 
	
		
			
			|  | 189 | +  _heatGridsErr.resize(10);
 | 
	
		
			
			|  | 190 | +
 | 
	
		
			
			|  | 191 | +  for (size_t i = 0; i < 10; i++) {
 | 
	
		
			
			|  | 192 | +    _heatGridsOk[i] = (util::geo::Grid<osmfixer::Cluster, util::geo::Point, double>(
 | 
	
		
			
			|  | 193 | +        5000, 5000, _bbox, false));
 | 
	
		
			
			|  | 194 | +    _heatGridsSugg[i] = (util::geo::Grid<osmfixer::Cluster, util::geo::Point, double>(
 | 
	
		
			
			|  | 195 | +        5000, 5000, _bbox, false));
 | 
	
		
			
			|  | 196 | +    _heatGridsErr[i] = (util::geo::Grid<osmfixer::Cluster, util::geo::Point, double>(
 | 
	
		
			
			|  | 197 | +        5000, 5000, _bbox, false));
 | 
	
		
			
			|  | 198 | +    int step = 2000 * (i + 1);
 | 
	
		
			
			|  | 199 | +
 | 
	
		
			
			|  | 200 | +    for (size_t x = 1; x * step < (_sgrid.getXWidth() * gSize); x++) {
 | 
	
		
			
			|  | 201 | +      for (size_t y = 1; y * step < (_sgrid.getYHeight() * gSize); y++) {
 | 
	
		
			
			|  | 202 | +        std::set<size_t> tmp;
 | 
	
		
			
			|  | 203 | +        auto reqBox = util::geo::DBox({_bbox.getLowerLeft().getX() + (x - 1) * step, _bbox.getLowerLeft().getY() + (y - 1) * step}, {_bbox.getLowerLeft().getX() + x * step, _bbox.getLowerLeft().getY() + y * step});
 | 
	
		
			
			|  | 204 | +        _sgrid.get(reqBox, &tmp);
 | 
	
		
			
			|  | 205 | +
 | 
	
		
			
			|  | 206 | +        size_t countOk = 0;
 | 
	
		
			
			|  | 207 | +        double avgXOk = 0;
 | 
	
		
			
			|  | 208 | +        double avgYOk = 0;
 | 
	
		
			
			|  | 209 | +
 | 
	
		
			
			|  | 210 | +        size_t countSugg = 0;
 | 
	
		
			
			|  | 211 | +        double avgXSugg = 0;
 | 
	
		
			
			|  | 212 | +        double avgYSugg = 0;
 | 
	
		
			
			|  | 213 | +
 | 
	
		
			
			|  | 214 | +        size_t countErr = 0;
 | 
	
		
			
			|  | 215 | +        double avgXErr = 0;
 | 
	
		
			
			|  | 216 | +        double avgYErr = 0;
 | 
	
		
			
			|  | 217 | +
 | 
	
		
			
			|  | 218 | +        for (auto j : tmp) {
 | 
	
		
			
			|  | 219 | +          const auto& stat = _stations[j];
 | 
	
		
			
			|  | 220 | +          if (stat.suggestions.size() == 0 && stat.attrErrs.size() == 0) {
 | 
	
		
			
			|  | 221 | +            if (util::geo::contains(stat.pos, reqBox)) {
 | 
	
		
			
			|  | 222 | +              countOk++;
 | 
	
		
			
			|  | 223 | +              avgXOk += stat.pos.getX();
 | 
	
		
			
			|  | 224 | +              avgYOk += stat.pos.getY();
 | 
	
		
			
			|  | 225 | +            }
 | 
	
		
			
			|  | 226 | +          } else if (stat.suggestions.size() > 0 && stat.attrErrs.size() == 0) {
 | 
	
		
			
			|  | 227 | +            if (util::geo::contains(stat.pos, reqBox)) {
 | 
	
		
			
			|  | 228 | +              countSugg++;
 | 
	
		
			
			|  | 229 | +              avgXSugg += stat.pos.getX();
 | 
	
		
			
			|  | 230 | +              avgYSugg += stat.pos.getY();
 | 
	
		
			
			|  | 231 | +            }
 | 
	
		
			
			|  | 232 | +          } else if (stat.attrErrs.size() > 0) {
 | 
	
		
			
			|  | 233 | +            if (util::geo::contains(stat.pos, reqBox)) {
 | 
	
		
			
			|  | 234 | +              countErr++;
 | 
	
		
			
			|  | 235 | +              avgXErr += stat.pos.getX();
 | 
	
		
			
			|  | 236 | +              avgYErr += stat.pos.getY();
 | 
	
		
			
			|  | 237 | +            }
 | 
	
		
			
			|  | 238 | +          }
 | 
	
		
			
			|  | 239 | +        }
 | 
	
		
			
			|  | 240 | +
 | 
	
		
			
			|  | 241 | +        if (countOk != 0) {
 | 
	
		
			
			|  | 242 | +          avgXOk /= countOk;
 | 
	
		
			
			|  | 243 | +          avgYOk /= countOk;
 | 
	
		
			
			|  | 244 | +          auto ll = util::geo::webMercToLatLng<double>(avgXOk, avgYOk);
 | 
	
		
			
			|  | 245 | +
 | 
	
		
			
			|  | 246 | +          _heatGridsOk[i].add({avgXOk, avgYOk}, Cluster{{avgXOk, avgYOk}, ll, countOk});
 | 
	
		
			
			|  | 247 | +        }
 | 
	
		
			
			|  | 248 | +
 | 
	
		
			
			|  | 249 | +        if (countSugg != 0) {
 | 
	
		
			
			|  | 250 | +          avgXSugg /= countSugg;
 | 
	
		
			
			|  | 251 | +          avgYSugg /= countSugg;
 | 
	
		
			
			|  | 252 | +          auto ll = util::geo::webMercToLatLng<double>(avgXSugg, avgYSugg);
 | 
	
		
			
			|  | 253 | +
 | 
	
		
			
			|  | 254 | +          _heatGridsSugg[i].add({avgXSugg, avgYSugg}, Cluster{{avgXSugg, avgYSugg}, ll, countSugg});
 | 
	
		
			
			|  | 255 | +        }
 | 
	
		
			
			|  | 256 | +
 | 
	
		
			
			|  | 257 | +        if (countErr != 0) {
 | 
	
		
			
			|  | 258 | +          avgXErr /= countErr;
 | 
	
		
			
			|  | 259 | +          avgYErr /= countErr;
 | 
	
		
			
			|  | 260 | +          auto ll = util::geo::webMercToLatLng<double>(avgXErr, avgYErr);
 | 
	
		
			
			|  | 261 | +
 | 
	
		
			
			|  | 262 | +          _heatGridsErr[i].add({avgXErr, avgYErr}, Cluster{{avgXErr, avgYErr}, ll, countErr});
 | 
	
		
			
			|  | 263 | +        }
 | 
	
		
			
			|  | 264 | +      }
 | 
	
		
			
			|  | 265 | +    }
 | 
	
		
			
			|  | 266 | +  }
 | 
	
		
			
			| 163 | 267 |  }
 | 
	
		
			
			| 164 | 268 |  
 | 
	
		
			
			| 165 | 269 |  // _____________________________________________________________________________
 | 
	
	
		
			
			|  | @@ -196,7 +300,7 @@ std::vector<const Group*> StatIdx::getGroups(const util::geo::DBox bbox) const {
 | 
	
		
			
			| 196 | 300 |    _ggrid.get(reqBox, &tmp);
 | 
	
		
			
			| 197 | 301 |  
 | 
	
		
			
			| 198 | 302 |    for (auto i : tmp) {
 | 
	
		
			
			| 199 |  | -    if (util::geo::intersects(_groups[i].poly, reqBox))
 | 
	
		
			
			|  | 303 | +    // if (util::geo::intersects(_groups[i].poly, reqBox))
 | 
	
		
			
			| 200 | 304 |        ret.push_back(&_groups[i]);
 | 
	
		
			
			| 201 | 305 |    }
 | 
	
		
			
			| 202 | 306 |  
 | 
	
	
		
			
			|  | @@ -231,6 +335,83 @@ const Station* StatIdx::getStation(size_t id) const {
 | 
	
		
			
			| 231 | 335 |  }
 | 
	
		
			
			| 232 | 336 |  
 | 
	
		
			
			| 233 | 337 |  // _____________________________________________________________________________
 | 
	
		
			
			|  | 338 | +std::vector<osmfixer::Cluster> StatIdx::getHeatGridErr(const util::geo::DBox bbox,
 | 
	
		
			
			|  | 339 | +                                                    size_t z) const {
 | 
	
		
			
			|  | 340 | +  auto ll = util::geo::latLngToWebMerc<double>(bbox.getLowerLeft().getX(),
 | 
	
		
			
			|  | 341 | +                                               bbox.getLowerLeft().getY());
 | 
	
		
			
			|  | 342 | +  auto ur = util::geo::latLngToWebMerc<double>(bbox.getUpperRight().getX(),
 | 
	
		
			
			|  | 343 | +                                               bbox.getUpperRight().getY());
 | 
	
		
			
			|  | 344 | +
 | 
	
		
			
			|  | 345 | +  auto reqBox = util::geo::DBox(ll, ur);
 | 
	
		
			
			|  | 346 | +
 | 
	
		
			
			|  | 347 | +  z = fmin(9, z);
 | 
	
		
			
			|  | 348 | +  z = fmax(0, z);
 | 
	
		
			
			|  | 349 | +
 | 
	
		
			
			|  | 350 | +  const auto& grid = _heatGridsErr[9 - z];
 | 
	
		
			
			|  | 351 | +  std::vector<osmfixer::Cluster> ret;
 | 
	
		
			
			|  | 352 | +
 | 
	
		
			
			|  | 353 | +  std::set<osmfixer::Cluster> tmp;
 | 
	
		
			
			|  | 354 | +  grid.get(reqBox, &tmp);
 | 
	
		
			
			|  | 355 | +
 | 
	
		
			
			|  | 356 | +  for (const auto& i : tmp) {
 | 
	
		
			
			|  | 357 | +    if (util::geo::contains(i.pos, reqBox)) ret.push_back(i);
 | 
	
		
			
			|  | 358 | +  }
 | 
	
		
			
			|  | 359 | +
 | 
	
		
			
			|  | 360 | +  return ret;
 | 
	
		
			
			|  | 361 | +}
 | 
	
		
			
			|  | 362 | +
 | 
	
		
			
			|  | 363 | +// _____________________________________________________________________________
 | 
	
		
			
			|  | 364 | +std::vector<osmfixer::Cluster> StatIdx::getHeatGridOk(const util::geo::DBox bbox,
 | 
	
		
			
			|  | 365 | +                                                    size_t z) const {
 | 
	
		
			
			|  | 366 | +  auto ll = util::geo::latLngToWebMerc<double>(bbox.getLowerLeft().getX(),
 | 
	
		
			
			|  | 367 | +                                               bbox.getLowerLeft().getY());
 | 
	
		
			
			|  | 368 | +  auto ur = util::geo::latLngToWebMerc<double>(bbox.getUpperRight().getX(),
 | 
	
		
			
			|  | 369 | +                                               bbox.getUpperRight().getY());
 | 
	
		
			
			|  | 370 | +
 | 
	
		
			
			|  | 371 | +  auto reqBox = util::geo::DBox(ll, ur);
 | 
	
		
			
			|  | 372 | +
 | 
	
		
			
			|  | 373 | +  z = fmin(9, z);
 | 
	
		
			
			|  | 374 | +  z = fmax(0, z);
 | 
	
		
			
			|  | 375 | +
 | 
	
		
			
			|  | 376 | +  const auto& grid = _heatGridsOk[9 - z];
 | 
	
		
			
			|  | 377 | +  std::vector<osmfixer::Cluster> ret;
 | 
	
		
			
			|  | 378 | +
 | 
	
		
			
			|  | 379 | +  std::set<osmfixer::Cluster> tmp;
 | 
	
		
			
			|  | 380 | +  grid.get(reqBox, &tmp);
 | 
	
		
			
			|  | 381 | +
 | 
	
		
			
			|  | 382 | +  for (const auto& i : tmp) {
 | 
	
		
			
			|  | 383 | +    if (util::geo::contains(i.pos, reqBox)) ret.push_back(i);
 | 
	
		
			
			|  | 384 | +  }
 | 
	
		
			
			|  | 385 | +
 | 
	
		
			
			|  | 386 | +  return ret;
 | 
	
		
			
			|  | 387 | +}
 | 
	
		
			
			|  | 388 | +
 | 
	
		
			
			|  | 389 | +// _____________________________________________________________________________
 | 
	
		
			
			|  | 390 | +std::vector<osmfixer::Cluster> StatIdx::getHeatGridSugg(const util::geo::DBox bbox,
 | 
	
		
			
			|  | 391 | +                                                    size_t z) const {
 | 
	
		
			
			|  | 392 | +  auto ll = util::geo::latLngToWebMerc<double>(bbox.getLowerLeft().getX(),
 | 
	
		
			
			|  | 393 | +                                               bbox.getLowerLeft().getY());
 | 
	
		
			
			|  | 394 | +  auto ur = util::geo::latLngToWebMerc<double>(bbox.getUpperRight().getX(),
 | 
	
		
			
			|  | 395 | +                                               bbox.getUpperRight().getY());
 | 
	
		
			
			|  | 396 | +
 | 
	
		
			
			|  | 397 | +  auto reqBox = util::geo::DBox(ll, ur);
 | 
	
		
			
			|  | 398 | +
 | 
	
		
			
			|  | 399 | +  z = fmin(9, z);
 | 
	
		
			
			|  | 400 | +  z = fmax(0, z);
 | 
	
		
			
			|  | 401 | +
 | 
	
		
			
			|  | 402 | +  const auto& grid = _heatGridsSugg[9 - z];
 | 
	
		
			
			|  | 403 | +  std::vector<osmfixer::Cluster> ret;
 | 
	
		
			
			|  | 404 | +
 | 
	
		
			
			|  | 405 | +  std::set<osmfixer::Cluster> tmp;
 | 
	
		
			
			|  | 406 | +  grid.get(reqBox, &tmp);
 | 
	
		
			
			|  | 407 | +
 | 
	
		
			
			|  | 408 | +  for (const auto& i : tmp) {
 | 
	
		
			
			|  | 409 | +    if (util::geo::contains(i.pos, reqBox)) ret.push_back(i);
 | 
	
		
			
			|  | 410 | +  }
 | 
	
		
			
			|  | 411 | +
 | 
	
		
			
			|  | 412 | +  return ret;
 | 
	
		
			
			|  | 413 | +}
 | 
	
		
			
			|  | 414 | +// _____________________________________________________________________________
 | 
	
		
			
			| 234 | 415 |  void StatIdx::initSuggestions() {
 | 
	
		
			
			| 235 | 416 |    for (size_t i = 0; i < _stations.size(); i++) {
 | 
	
		
			
			| 236 | 417 |      auto& stat = _stations[i];
 | 
	
	
		
			
			|  | @@ -261,7 +442,8 @@ void StatIdx::initSuggestions() {
 | 
	
		
			
			| 261 | 442 |            stat.suggestions.push_back(_suggestions.size() - 1);
 | 
	
		
			
			| 262 | 443 |          }
 | 
	
		
			
			| 263 | 444 |        } else {
 | 
	
		
			
			| 264 |  | -        if (getGroup(stat.group)->osmid == 1) {
 | 
	
		
			
			|  | 445 | +        if (getGroup(stat.group)->osmid == 1 &&
 | 
	
		
			
			|  | 446 | +            getGroup(stat.group)->stations.size() > 1) {
 | 
	
		
			
			| 265 | 447 |            // move station from relation into new group
 | 
	
		
			
			| 266 | 448 |            sug.type = 3;
 | 
	
		
			
			| 267 | 449 |            sug.orig_gid = stat.origGroup;
 |