Patrick Brosi 4 gadi atpakaļ
vecāks
revīzija
9023a6c07a

+ 45 - 6
src/osmfixer/FixerMain.cpp

@@ -8,8 +8,16 @@
8 8
 #include "util/http/Server.h"
9 9
 #include "util/log/Log.h"
10 10
 
11
-using osmfixer::StatServer;
11
+using osmfixer::IdRange;
12 12
 using osmfixer::StatIdx;
13
+using osmfixer::StatServer;
14
+
15
+// _____________________________________________________________________________
16
+void printHelp(int argc, char** argv) {
17
+  UNUSED(argc);
18
+  std::cout << "Usage: " << argv[0] << " [-p <port>] [--help] [-h] <fix input files>" << "\n";
19
+  std::cout << "\nAllowed arguments:\n    -p <port>  Port for server to listen to\n";
20
+}
13 21
 
14 22
 // _____________________________________________________________________________
15 23
 int main(int argc, char** argv) {
@@ -21,14 +29,44 @@ int main(int argc, char** argv) {
21 29
 
22 30
   int port = 9090;
23 31
 
24
-  if (argc < 2) {
25
-    LOG(ERROR) << "Missing path to stations file.";
32
+  std::vector<std::string> inputPaths;
33
+
34
+  for (int i = 1; i < argc; i++) {
35
+    std::string cur = argv[i];
36
+    if (cur == "-h" || cur == "--help") {
37
+      printHelp(argc, argv);
38
+      exit(0);
39
+    } else if (cur == "-p") {
40
+      if (++i >= argc) {
41
+        LOG(ERROR) << "Missing argument for port (-p).";
42
+        exit(1);
43
+      }
44
+      port = atoi(argv[i]);
45
+    } else {
46
+      inputPaths.push_back(cur);
47
+    }
48
+  }
49
+
50
+  if (inputPaths.size() == 0) {
51
+    LOG(ERROR) << "Missing path(s) to stations file(s).";
52
+    printHelp(argc, argv);
26 53
     exit(1);
27 54
   }
28 55
 
29
-  std::vector<StatIdx> idx(argc - 1);
30
-  for(int i = 1; i < argc; i++) {
31
-    idx[i-1].readFromFile(argv[i]);
56
+  std::vector<std::pair<IdRange, StatIdx>> idx(inputPaths.size());
57
+  IdRange idRange{0, 0, 0, 0, 0, 0};
58
+  for (size_t i = 0; i < inputPaths.size(); i++) {
59
+    idx[i].first = idRange;
60
+    if (i > 0) {
61
+      idx[i].first.sidStart = idx[i - 1].first.sidEnd + 1;
62
+      idx[i].first.gidStart = idx[i - 1].first.gidEnd + 1;
63
+      idx[i].first.suggIdStart = idx[i - 1].first.suggIdStart + 1;
64
+    }
65
+    idx[i].second.readFromFile(inputPaths[i]);
66
+    idx[i].first.sidEnd = idx[i].first.sidStart + idx[i].second.maxSId() + 1;
67
+    idx[i].first.gidEnd = idx[i].first.gidStart + idx[i].second.maxGId() + 1;
68
+    idx[i].first.suggIdEnd =
69
+        idx[i].first.suggIdStart + idx[i].second.maxSuggId() + 1;
32 70
   }
33 71
 
34 72
   LOG(INFO) << "Starting server...";
@@ -37,3 +75,4 @@ int main(int argc, char** argv) {
37 75
   LOG(INFO) << "Listening on port " << port;
38 76
   util::http::HttpServer(port, &serv).run();
39 77
 }
78
+

+ 4 - 0
src/osmfixer/index/StatIdx.h

@@ -63,6 +63,10 @@ class StatIdx {
63 63
 
64 64
   void readFromFile(const std::string& path);
65 65
 
66
+  size_t maxSId() const { return _stations.size() - 1; };
67
+  size_t maxGId() const { return _groups.size() - 1; };
68
+  size_t maxSuggId() const { return _suggestions.size() - 1; };
69
+
66 70
   std::vector<const Station*> getStations(const util::geo::DBox bbox) const;
67 71
   std::vector<const Group*> getGroups(const util::geo::DBox bbox) const;
68 72
   std::vector<const Suggestion*> getSuggestions(

+ 128 - 68
src/osmfixer/server/StatServer.cpp

@@ -3,13 +3,13 @@
3 3
 // Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
4 4
 
5 5
 #include <vector>
6
+#include "osmfixer/build.h"
7
+#include "osmfixer/index.h"
6 8
 #include "osmfixer/server/StatServer.h"
7 9
 #include "util/Misc.h"
8 10
 #include "util/String.h"
9 11
 #include "util/geo/Geo.h"
10 12
 #include "util/log/Log.h"
11
-#include "osmfixer/index.h"
12
-#include "osmfixer/build.h"
13 13
 
14 14
 using osmfixer::Params;
15 15
 using osmfixer::StatServer;
@@ -24,10 +24,15 @@ util::http::Answer StatServer::handle(const util::http::Req& req,
24 24
     auto cmd = parseUrl(req.url, req.payload, &params);
25 25
 
26 26
     if (cmd == "/") {
27
-      a = util::http::Answer("200 OK", std::string(index_html, index_html + sizeof index_html / sizeof index_html[0]));
27
+      a = util::http::Answer(
28
+          "200 OK",
29
+          std::string(index_html,
30
+                      index_html + sizeof index_html / sizeof index_html[0]));
28 31
       a.params["Content-Type"] = "text/html; charset=utf-8";
29 32
     } else if (cmd == "/build.js") {
30
-      a = util::http::Answer("200 OK", std::string(build_js, build_js + sizeof build_js / sizeof build_js[0]));
33
+      a = util::http::Answer(
34
+          "200 OK", std::string(build_js, build_js + sizeof build_js /
35
+                                                         sizeof build_js[0]));
31 36
       a.params["Content-Type"] = "application/javascript; charset=utf-8";
32 37
     } else if (cmd == "/map") {
33 38
       a = handleMapReq(params);
@@ -90,43 +95,49 @@ util::http::Answer StatServer::handleHeatMapReq(const Params& pars) const {
90 95
   json << "{\"ok\":[";
91 96
 
92 97
   char sep = ' ';
93
-  for (const auto& idx : _idxs) {
98
+  for (const auto& idxPair : _idxs) {
99
+    const auto& idx = idxPair.second;
100
+
94 101
     auto hgOk = idx.getHeatGridOk(bbox, z);
95 102
     for (auto cluster : hgOk) {
96 103
       json << sep;
97 104
       sep = ',';
98 105
 
99
-      json << "[" << cluster.latLng.getY() << "," << cluster.latLng.getX() << ","
100
-           << cluster.size << "]";
106
+      json << "[" << cluster.latLng.getY() << "," << cluster.latLng.getX()
107
+           << "," << cluster.size << "]";
101 108
     }
102 109
   }
103 110
 
104 111
   json << "],\"sugg\":[";
105 112
 
106 113
   sep = ' ';
107
-  for (const auto& idx : _idxs) {
114
+  for (const auto& idxPair : _idxs) {
115
+    const auto& idx = idxPair.second;
116
+
108 117
     auto hgSugg = idx.getHeatGridSugg(bbox, z);
109 118
 
110 119
     for (auto cluster : hgSugg) {
111 120
       json << sep;
112 121
       sep = ',';
113 122
 
114
-      json << "[" << cluster.latLng.getY() << "," << cluster.latLng.getX() << ","
115
-           << cluster.size << "]";
123
+      json << "[" << cluster.latLng.getY() << "," << cluster.latLng.getX()
124
+           << "," << cluster.size << "]";
116 125
     }
117 126
   }
118 127
 
119 128
   json << "],\"err\":[";
120 129
 
121 130
   sep = ' ';
122
-  for (const auto& idx : _idxs) {
131
+  for (const auto& idxPair : _idxs) {
132
+    const auto& idx = idxPair.second;
133
+
123 134
     auto hgErr = idx.getHeatGridErr(bbox, z);
124 135
     for (auto cluster : hgErr) {
125 136
       json << sep;
126 137
       sep = ',';
127 138
 
128
-      json << "[" << cluster.latLng.getY() << "," << cluster.latLng.getX() << ","
129
-           << cluster.size << "]";
139
+      json << "[" << cluster.latLng.getY() << "," << cluster.latLng.getX()
140
+           << "," << cluster.size << "]";
130 141
     }
131 142
   }
132 143
 
@@ -148,9 +159,6 @@ util::http::Answer StatServer::handleMapReq(const Params& pars) const {
148 159
   if (pars.count("cb")) cb = pars.find("cb")->second.c_str();
149 160
   auto box = util::split(pars.find("bbox")->second, ',');
150 161
 
151
-  // TODO!
152
-  size_t did = 0;
153
-
154 162
   if (box.size() != 4) throw std::invalid_argument("Invalid request.");
155 163
 
156 164
   double lat1 = atof(box[0].c_str());
@@ -169,7 +177,9 @@ util::http::Answer StatServer::handleMapReq(const Params& pars) const {
169 177
   json << "{\"stats\":[";
170 178
 
171 179
   char sep = ' ';
172
-  for (const auto& idx : _idxs) {
180
+  for (size_t did = 0; did < _idxs.size(); did++) {
181
+    const auto& idx = _idxs[did].second;
182
+
173 183
     auto ret = idx.getStations(bbox);
174 184
     for (auto stat : ret) {
175 185
       json << sep;
@@ -180,24 +190,28 @@ util::http::Answer StatServer::handleMapReq(const Params& pars) const {
180 190
 
181 191
   json << "], \"groups\":[";
182 192
   sep = ' ';
183
-  for (const auto& idx : _idxs) {
193
+  for (size_t did = 0; did < _idxs.size(); did++) {
194
+    const auto& idx = _idxs[did].second;
195
+
184 196
     auto gret = idx.getGroups(bbox);
185 197
     for (auto group : gret) {
186 198
       if (group->polyStations.size() == 1 && group->osmid < 2) continue;
187 199
       json << sep;
188 200
       sep = ',';
189
-      printGroup(group, &json);
201
+      printGroup(group, did, &json);
190 202
     }
191 203
   }
192 204
 
193 205
   json << "], \"su\":[";
194 206
   sep = ' ';
195
-  for (const auto& idx : _idxs) {
207
+  for (size_t did = 0; did < _idxs.size(); did++) {
208
+    const auto& idx = _idxs[did].second;
209
+
196 210
     auto suggs = idx.getSuggestions(bbox);
197 211
     for (auto sugg : suggs) {
198 212
       json << sep;
199 213
       sep = ',';
200
-      printSugg(sugg, &json);
214
+      printSugg(sugg, did, &json);
201 215
     }
202 216
   }
203 217
 
@@ -212,13 +226,17 @@ util::http::Answer StatServer::handleMapReq(const Params& pars) const {
212 226
 }
213 227
 
214 228
 // _____________________________________________________________________________
215
-void StatServer::printStation(const Station* stat, size_t did, std::ostream* out) const {
229
+void StatServer::printStation(const Station* stat, size_t did,
230
+                              std::ostream* out) const {
231
+  const auto& idx = _idxs[did].second;
232
+  const auto& range = _idxs[did].first;
233
+
216 234
   auto latLng =
217 235
       util::geo::webMercToLatLng<double>(stat->pos.getX(), stat->pos.getY());
218
-  (*out) << "{\"id\":" << stat->id << ",\"lat\":" << latLng.getY()
219
-         << ",\"lon\":" << latLng.getX();
236
+  (*out) << "{\"id\":" << stat->id + range.sidStart
237
+         << ",\"lat\":" << latLng.getY() << ",\"lon\":" << latLng.getX();
220 238
 
221
-  if (stat->origGroup != stat->group || _idxs[did].getGroup(stat->group)->osmid == 1)
239
+  if (stat->origGroup != stat->group || idx.getGroup(stat->group)->osmid == 1)
222 240
     (*out) << ",\"su\":1";
223 241
 
224 242
   if (stat->attrErrs.size())
@@ -227,15 +245,18 @@ void StatServer::printStation(const Station* stat, size_t did, std::ostream* out
227 245
 }
228 246
 
229 247
 // _____________________________________________________________________________
230
-void StatServer::printSugg(const Suggestion* sugg, std::ostream* out) {
248
+void StatServer::printSugg(const Suggestion* sugg, size_t did,
249
+                           std::ostream* out) const {
250
+  const auto& range = _idxs[did].first;
251
+
231 252
   std::vector<util::geo::DPoint> projArrow;
232 253
 
233 254
   for (auto p : sugg->arrow) {
234 255
     projArrow.push_back(util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
235 256
   }
236 257
 
237
-  (*out) << "{\"id\":" << sugg->station << ",\"type\":" << sugg->type
238
-         << ",\"arrow\":[";
258
+  (*out) << "{\"id\":" << sugg->station + range.suggIdStart
259
+         << ",\"type\":" << sugg->type << ",\"arrow\":[";
239 260
   char sep = ' ';
240 261
   for (auto p : projArrow) {
241 262
     (*out) << sep << "[" << p.getY() << "," << p.getX() << "]";
@@ -246,14 +267,17 @@ void StatServer::printSugg(const Suggestion* sugg, std::ostream* out) {
246 267
 }
247 268
 
248 269
 // _____________________________________________________________________________
249
-void StatServer::printGroup(const Group* group, std::ostream* out) {
270
+void StatServer::printGroup(const Group* group, size_t did,
271
+                            std::ostream* out) const {
272
+  const auto& range = _idxs[did].first;
273
+
250 274
   std::vector<util::geo::DPoint> projPoly;
251 275
 
252 276
   for (auto p : group->poly.getOuter()) {
253 277
     projPoly.push_back(util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
254 278
   }
255 279
 
256
-  (*out) << "{\"id\":" << group->id << ",\"poly\":[";
280
+  (*out) << "{\"id\":" << group->id + range.gidStart << ",\"poly\":[";
257 281
   char sep = ' ';
258 282
   for (auto p : projPoly) {
259 283
     (*out) << sep << "[" << p.getY() << "," << p.getX() << "]";
@@ -290,6 +314,39 @@ std::string StatServer::parseUrl(std::string u, std::string pl,
290 314
 }
291 315
 
292 316
 // _____________________________________________________________________________
317
+size_t StatServer::getDidBySid(size_t sid) const {
318
+  for (size_t i = 0; i < _idxs.size(); i++) {
319
+    auto idx = _idxs[i].second;
320
+    auto range = _idxs[i].first;
321
+    if (sid >= range.sidStart && sid <= range.sidEnd) return i;
322
+  }
323
+
324
+  return 0;
325
+}
326
+
327
+// _____________________________________________________________________________
328
+size_t StatServer::getDidBySuggid(size_t suggid) const {
329
+  for (size_t i = 0; i < _idxs.size(); i++) {
330
+    auto idx = _idxs[i].second;
331
+    auto range = _idxs[i].first;
332
+    if (suggid >= range.suggIdStart && suggid <= range.suggIdEnd) return i;
333
+  }
334
+
335
+  return 0;
336
+}
337
+
338
+// _____________________________________________________________________________
339
+size_t StatServer::getDidByGid(size_t gid) const {
340
+  for (size_t i = 0; i < _idxs.size(); i++) {
341
+    auto idx = _idxs[i].second;
342
+    auto range = _idxs[i].first;
343
+    if (gid >= range.gidStart && gid <= range.gidEnd) return i;
344
+  }
345
+
346
+  return 0;
347
+}
348
+
349
+// _____________________________________________________________________________
293 350
 util::http::Answer StatServer::handleGroupReq(const Params& pars) const {
294 351
   if (pars.count("id") == 0 || pars.find("id")->second.empty())
295 352
     throw std::invalid_argument("No ID specified.");
@@ -298,10 +355,13 @@ util::http::Answer StatServer::handleGroupReq(const Params& pars) const {
298 355
 
299 356
   size_t gid = atol(pars.find("id")->second.c_str());
300 357
 
301
-  // TODO!
302
-  size_t did=0;
358
+  size_t did = getDidByGid(gid);
359
+
360
+  const auto& range = _idxs[did].first;
361
+  const auto& idx = _idxs[did].second;
362
+  size_t idxGid = gid - range.gidStart;
303 363
 
304
-  auto group = _idxs[did].getGroup(gid);
364
+  auto group = idx.getGroup(idxGid);
305 365
 
306 366
   if (!group) return util::http::Answer("404 Not Found", "Group not found.");
307 367
 
@@ -336,10 +396,10 @@ util::http::Answer StatServer::handleGroupReq(const Params& pars) const {
336 396
   for (const auto& sid : group->stations) {
337 397
     json << sep;
338 398
     sep = ',';
339
-    const auto stat = _idxs[did].getStation(sid);
340
-    json << "{\"id\":" << sid << ","
399
+    const auto stat = idx.getStation(sid);
400
+    json << "{\"id\":" << sid + range.sidStart << ","
341 401
          << "\"osmid\":" << stat->osmid << ","
342
-         << "\"group\":" << stat->group << ","
402
+         << "\"group\":" << stat->group + range.gidStart << ","
343 403
          << "\"orig_group\":" << stat->origGroup << ","
344 404
          << "\"attrs\":{";
345 405
 
@@ -381,10 +441,14 @@ util::http::Answer StatServer::handleStatReq(const Params& pars) const {
381 441
   if (pars.count("cb")) cb = pars.find("cb")->second.c_str();
382 442
 
383 443
   size_t sid = atol(pars.find("id")->second.c_str());
384
-  // TODO!
385
-  size_t did=0;
386 444
 
387
-  auto stat = _idxs[did].getStation(sid);
445
+  size_t did = getDidBySid(sid);
446
+
447
+  const auto& range = _idxs[did].first;
448
+  const auto& idx = _idxs[did].second;
449
+  size_t idxSid = sid - range.sidStart;
450
+
451
+  auto stat = idx.getStation(idxSid);
388 452
 
389 453
   if (!stat) return util::http::Answer("404 Not Found", "Station not found.");
390 454
 
@@ -421,14 +485,14 @@ util::http::Answer StatServer::handleStatReq(const Params& pars) const {
421 485
   for (const auto& err : stat->attrErrs) {
422 486
     json << sep;
423 487
     sep = ',';
424
-    const auto otherStat = _idxs[did].getStation(err.otherId);
488
+    const auto otherStat = idx.getStation(err.otherId);
425 489
     json << "{";
426 490
     json << "\"attr\":[\"" << err.attr << "\",\"" << stat->attrs.at(err.attr)[0]
427 491
          << "\"]";
428 492
     json << ",\"other_attr\":[\"" << err.otherAttr << "\",\""
429 493
          << otherStat->attrs.at(err.otherAttr)[0] << "\"]";
430 494
     json << ",\"conf\":" << err.conf;
431
-    json << ",\"other\":" << err.otherId;
495
+    json << ",\"other\":" << err.otherId + range.sidStart;
432 496
     json << ",\"other_osmid\":" << otherStat->osmid;
433 497
     json << "}";
434 498
   }
@@ -440,40 +504,36 @@ util::http::Answer StatServer::handleStatReq(const Params& pars) const {
440 504
   char seper = ' ';
441 505
 
442 506
   // suggestions
443
-  if (stat->group != stat->origGroup ||
444
-      _idxs[did].getGroup(stat->group)->osmid == 1) {
445
-    if (_idxs[did].getGroup(stat->origGroup)->osmid < 2) {
446
-      if (_idxs[did].getGroup(stat->group)->osmid == 1) {
447
-        json << "{\"type\": 1, \"target_gid\":" << stat->group << "}";
507
+  if (stat->group != stat->origGroup || idx.getGroup(stat->group)->osmid == 1) {
508
+    if (idx.getGroup(stat->origGroup)->osmid < 2) {
509
+      if (idx.getGroup(stat->group)->osmid == 1) {
510
+        json << "{\"type\": 1, \"target_gid\":" << stat->group + range.gidStart
511
+             << "}";
448 512
         seper = ',';
449
-      } else if (_idxs[did].getGroup(stat->group)->osmid > 1) {
450
-        json << "{\"type\": 2, \"target_gid\":" << stat->group
451
-             << ",\"target_osm_rel_id\":" << _idxs[did].getGroup(stat->group)->osmid
513
+      } else if (idx.getGroup(stat->group)->osmid > 1) {
514
+        json << "{\"type\": 2, \"target_gid\":" << stat->group + range.gidStart
515
+             << ",\"target_osm_rel_id\":" << idx.getGroup(stat->group)->osmid
452 516
              << "}";
453 517
         seper = ',';
454 518
       }
455 519
     } else {
456
-      if (_idxs[did].getGroup(stat->group)->osmid == 1 &&
457
-          _idxs[did].getGroup(stat->group)->stations.size() > 1) {
458
-        json << "{\"type\": 3,\"orig_gid\":" << stat->origGroup
459
-             << ",\"orig_osm_rel_id\":"
460
-             << _idxs[did].getGroup(stat->origGroup)->osmid
461
-             << ",\"target_gid\":" << stat->group << "}";
520
+      if (idx.getGroup(stat->group)->osmid == 1 &&
521
+          idx.getGroup(stat->group)->stations.size() > 1) {
522
+        json << "{\"type\": 3,\"orig_gid\":" << stat->origGroup + range.gidStart
523
+             << ",\"orig_osm_rel_id\":" << idx.getGroup(stat->origGroup)->osmid
524
+             << ",\"target_gid\":" << stat->group + range.gidStart << "}";
462 525
         seper = ',';
463
-      } else if (_idxs[did].getGroup(stat->group)->osmid > 1) {
464
-        json << "{\"type\": 4,\"orig_gid\":" << stat->origGroup
465
-             << ",\"orig_osm_rel_id\":"
466
-             << _idxs[did].getGroup(stat->origGroup)->osmid
467
-             << ",\"target_gid\":" << stat->group
468
-             << ",\"target_osm_rel_id\":" << _idxs[did].getGroup(stat->group)->osmid
469
-             << "}";
526
+      } else if (idx.getGroup(stat->group)->osmid > 1) {
527
+        json << "{\"type\": 4,\"orig_gid\":" << stat->origGroup + range.gidStart
528
+             << ",\"orig_osm_rel_id\":" << idx.getGroup(stat->origGroup)->osmid
529
+             << ",\"target_gid\":" << stat->group << ",\"target_osm_rel_id\":"
530
+             << idx.getGroup(stat->group)->osmid + range.gidStart << "}";
470 531
         seper = ',';
471 532
       } else {
472
-        json << "{\"type\": 5,\"orig_gid\":" << stat->origGroup
473
-             << ",\"orig_osm_rel_id\":"
474
-             << _idxs[did].getGroup(stat->origGroup)->osmid
475
-             << ",\"target_gid\":" << stat->group
476
-             << ",\"target_osm_rel_id\":" << _idxs[did].getGroup(stat->group)->osmid
533
+        json << "{\"type\": 5,\"orig_gid\":" << stat->origGroup + range.gidStart
534
+             << ",\"orig_osm_rel_id\":" << idx.getGroup(stat->origGroup)->osmid
535
+             << ",\"target_gid\":" << stat->group + range.gidStart
536
+             << ",\"target_osm_rel_id\":" << idx.getGroup(stat->group)->osmid
477 537
              << "}";
478 538
         seper = ',';
479 539
       }
@@ -481,7 +541,7 @@ util::http::Answer StatServer::handleStatReq(const Params& pars) const {
481 541
   }
482 542
 
483 543
   for (auto attrErr : stat->attrErrs) {
484
-    const auto otherStat = _idxs[did].getStation(attrErr.otherId);
544
+    const auto otherStat = idx.getStation(attrErr.otherId);
485 545
     if (otherStat->osmid == stat->osmid) {
486 546
       // fix attributes
487 547
       json << seper << "{\"type\": 6,\"attr\":\"" << attrErr.attr << "\""

+ 13 - 4
src/osmfixer/server/StatServer.h

@@ -12,11 +12,16 @@
12 12
 
13 13
 namespace osmfixer {
14 14
 
15
+struct IdRange {
16
+  size_t sidStart, sidEnd, gidStart, gidEnd, suggIdStart, suggIdEnd;
17
+};
18
+
15 19
 typedef std::map<std::string, std::string> Params;
16 20
 
17 21
 class StatServer : public util::http::Handler {
18 22
  public:
19
-  explicit StatServer(const std::vector<osmfixer::StatIdx>& idxs)
23
+  explicit StatServer(
24
+      const std::vector<std::pair<IdRange, osmfixer::StatIdx>>& idxs)
20 25
       : _idxs(idxs) {}
21 26
 
22 27
   virtual util::http::Answer handle(const util::http::Req& request,
@@ -31,10 +36,14 @@ class StatServer : public util::http::Handler {
31 36
   util::http::Answer handleGroupReq(const Params& pars) const;
32 37
 
33 38
   void printStation(const Station* stat, size_t did, std::ostream* out) const;
34
-  static void printGroup(const Group* stat, std::ostream* out);
35
-  static void printSugg(const Suggestion* stat, std::ostream* out);
39
+  void printGroup(const Group* stat, size_t did, std::ostream* out) const;
40
+  void printSugg(const Suggestion* stat, size_t did, std::ostream* out) const;
41
+
42
+  size_t getDidBySid(size_t sid) const;
43
+  size_t getDidBySuggid(size_t suggid) const;
44
+  size_t getDidByGid(size_t gid) const;
36 45
 
37
-  const std::vector<osmfixer::StatIdx>& _idxs;
46
+  const std::vector<std::pair<IdRange, osmfixer::StatIdx>>& _idxs;
38 47
 };
39 48
 }  // namespace osmfixer
40 49