Browse Source

polygon support

Patrick Brosi 4 years ago
parent
commit
2c3ae38583

+ 117 - 69
src/osmfixer/index/StatIdx.cpp

@@ -30,27 +30,48 @@ void StatIdx::readFromFile(const std::string& path) {
30
     if (line.size() == 0) break;
30
     if (line.size() == 0) break;
31
 
31
 
32
     std::stringstream rec(line);
32
     std::stringstream rec(line);
33
-    size_t osmid, origGroup, group;
34
-    double lat, lng;
33
+    int64_t osmid, origGroup, group;
34
+    double lat = 0, lng = 0;
35
     OsmAttrs attrs;
35
     OsmAttrs attrs;
36
 
36
 
37
     rec >> osmid;
37
     rec >> osmid;
38
-    rec >> lat;
39
-    rec >> lng;
40
     rec >> origGroup;
38
     rec >> origGroup;
41
     rec >> group;
39
     rec >> group;
42
 
40
 
41
+    util::geo::DLine geom;
42
+    util::geo::DLine latLngs;
43
+
43
     std::string temp;
44
     std::string temp;
45
+    std::string key, val;
46
+    uint8_t sw = 0;
44
     std::getline(rec, temp, '\t');
47
     std::getline(rec, temp, '\t');
45
     while (std::getline(rec, temp, '\t')) {
48
     while (std::getline(rec, temp, '\t')) {
46
-      std::string key, val;
47
-      key = temp;
48
-      std::getline(rec, temp, '\t');
49
-      val = temp;
50
-      attrs[key].push_back(val);
49
+      if (sw == 2) {
50
+        std::getline(rec, val, '\t');
51
+        attrs[temp].push_back(val);
52
+      } else {
53
+        try {
54
+          double a = std::stod(temp);
55
+          if (sw == 1) {
56
+            lat = a;
57
+            geom.push_back(util::geo::latLngToWebMerc<double>(lat, lng));
58
+            sw = 0;
59
+          } else if (sw == 0) {
60
+            lng = a;
61
+            sw = 1;
62
+          }
63
+        } catch (std::invalid_argument) {
64
+          sw = 2;
65
+          std::getline(rec, val, '\t');
66
+          attrs[temp].push_back(val);
67
+        }
68
+      }
51
     }
69
     }
52
 
70
 
53
-    addStation(osmid, lat, lng, origGroup, group, attrs);
71
+    if (geom.size() > 1) geom = util::geo::simplify(geom, 0.5);
72
+    for (const auto& p : geom) latLngs.push_back(util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
73
+
74
+    addStation(osmid, geom, latLngs, origGroup, group, attrs);
54
   }
75
   }
55
   LOG(INFO) << "Done.";
76
   LOG(INFO) << "Done.";
56
 
77
 
@@ -61,7 +82,7 @@ void StatIdx::readFromFile(const std::string& path) {
61
     if (line.size() == 0) break;
82
     if (line.size() == 0) break;
62
 
83
 
63
     std::stringstream rec(line);
84
     std::stringstream rec(line);
64
-    size_t osmid;
85
+    int64_t osmid;
65
     OsmAttrs attrs;
86
     OsmAttrs attrs;
66
 
87
 
67
     rec >> osmid;
88
     rec >> osmid;
@@ -81,7 +102,7 @@ void StatIdx::readFromFile(const std::string& path) {
81
     size_t id;
102
     size_t id;
82
     std::string attr, otherAttr;
103
     std::string attr, otherAttr;
83
     double conf;
104
     double conf;
84
-    size_t otherId;
105
+    int64_t otherId;
85
 
106
 
86
     rec >> id;
107
     rec >> id;
87
 
108
 
@@ -105,23 +126,23 @@ void StatIdx::readFromFile(const std::string& path) {
105
 }
126
 }
106
 
127
 
107
 // _____________________________________________________________________________
128
 // _____________________________________________________________________________
108
-void StatIdx::addStation(size_t osmid, double lat, double lng, size_t origGroup,
129
+void StatIdx::addStation(int64_t osmid, const util::geo::DLine& geom,
130
+                         const util::geo::DLine& latLngs, size_t origGroup,
109
                          size_t group, const OsmAttrs& attrs) {
131
                          size_t group, const OsmAttrs& attrs) {
110
-  auto point = util::geo::latLngToWebMerc<double>(lat, lng);
111
-
112
-  _stations.emplace_back(
113
-      Station{_stations.size(),
114
-              osmid,
115
-              origGroup,
116
-              group,
117
-              point,
118
-              util::geo::webMercToLatLng<double>(point.getX(), point.getY()),
119
-              attrs,
120
-              {},
121
-              {}});
132
+  auto centroid = util::geo::centroid(latLngs);
133
+  _stations.emplace_back(Station{_stations.size(),
134
+                                 osmid,
135
+                                 origGroup,
136
+                                 group,
137
+                                 centroid,
138
+                                 geom,
139
+                                 latLngs,
140
+                                 attrs,
141
+                                 {},
142
+                                 {}});
122
 
143
 
123
   // extend bounding box
144
   // extend bounding box
124
-  _bbox = util::geo::extendBox(point, _bbox);
145
+  for (const auto& point : geom) _bbox = util::geo::extendBox(point, _bbox);
125
 }
146
 }
126
 
147
 
127
 // _____________________________________________________________________________
148
 // _____________________________________________________________________________
@@ -166,19 +187,27 @@ void StatIdx::initGroups() {
166
         double y = rad * sin((2.0 * M_PI / static_cast<double>(n)) *
187
         double y = rad * sin((2.0 * M_PI / static_cast<double>(n)) *
167
                              static_cast<double>(i));
188
                              static_cast<double>(i));
168
 
189
 
169
-        mp.push_back(util::geo::DPoint(_stations[stid].pos.getX() + x,
170
-                                       _stations[stid].pos.getY() + y));
190
+        for (const auto& geom : _stations[stid].pos) {
191
+          mp.push_back(util::geo::DPoint(geom.getX() + x, geom.getY() + y));
192
+        }
171
       }
193
       }
172
     }
194
     }
173
-    _groups[i].poly = util::geo::convexHull(mp);
195
+    _groups[i].poly = util::geo::simplify(util::geo::convexHull(mp).getOuter(), 0.5);
196
+
197
+    for (auto p : _groups[i].poly.getOuter()) {
198
+      _groups[i].llPoly.getOuter().push_back(
199
+          util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
200
+    }
201
+
202
+    _groups[i].centroid = util::geo::centroid(_groups[i].llPoly);
174
   }
203
   }
175
 }
204
 }
176
 
205
 
177
 // _____________________________________________________________________________
206
 // _____________________________________________________________________________
178
 void StatIdx::initIndex() {
207
 void StatIdx::initIndex() {
179
   double gSize = 10000;
208
   double gSize = 10000;
180
-  _sgrid = util::geo::Grid<size_t, util::geo::Point, double>(gSize, gSize,
181
-                                                             _bbox, false);
209
+  _sgrid = util::geo::Grid<size_t, util::geo::Polygon, double>(gSize, gSize,
210
+                                                               _bbox, false);
182
   _ggrid = util::geo::Grid<size_t, util::geo::Polygon, double>(gSize, gSize,
211
   _ggrid = util::geo::Grid<size_t, util::geo::Polygon, double>(gSize, gSize,
183
                                                                _bbox, false);
212
                                                                _bbox, false);
184
   _suggrid = util::geo::Grid<size_t, util::geo::Line, double>(gSize, gSize,
213
   _suggrid = util::geo::Grid<size_t, util::geo::Line, double>(gSize, gSize,
@@ -194,18 +223,25 @@ void StatIdx::initIndex() {
194
   _heatGridsErr.resize(10);
223
   _heatGridsErr.resize(10);
195
 
224
 
196
   for (size_t i = 0; i < 10; i++) {
225
   for (size_t i = 0; i < 10; i++) {
197
-    _heatGridsOk[i] = (util::geo::Grid<osmfixer::Cluster, util::geo::Point, double>(
198
-        5000, 5000, _bbox, false));
199
-    _heatGridsSugg[i] = (util::geo::Grid<osmfixer::Cluster, util::geo::Point, double>(
200
-        5000, 5000, _bbox, false));
201
-    _heatGridsErr[i] = (util::geo::Grid<osmfixer::Cluster, util::geo::Point, double>(
202
-        5000, 5000, _bbox, false));
226
+    _heatGridsOk[i] =
227
+        (util::geo::Grid<osmfixer::Cluster, util::geo::Point, double>(
228
+            5000, 5000, _bbox, false));
229
+    _heatGridsSugg[i] =
230
+        (util::geo::Grid<osmfixer::Cluster, util::geo::Point, double>(
231
+            5000, 5000, _bbox, false));
232
+    _heatGridsErr[i] =
233
+        (util::geo::Grid<osmfixer::Cluster, util::geo::Point, double>(
234
+            5000, 5000, _bbox, false));
203
     int step = 2000 * (i + 1);
235
     int step = 2000 * (i + 1);
204
 
236
 
205
     for (size_t x = 1; x * step < (_sgrid.getXWidth() * gSize); x++) {
237
     for (size_t x = 1; x * step < (_sgrid.getXWidth() * gSize); x++) {
206
       for (size_t y = 1; y * step < (_sgrid.getYHeight() * gSize); y++) {
238
       for (size_t y = 1; y * step < (_sgrid.getYHeight() * gSize); y++) {
207
         std::set<size_t> tmp;
239
         std::set<size_t> tmp;
208
-        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});
240
+        auto reqBox =
241
+            util::geo::DBox({_bbox.getLowerLeft().getX() + (x - 1) * step,
242
+                             _bbox.getLowerLeft().getY() + (y - 1) * step},
243
+                            {_bbox.getLowerLeft().getX() + x * step,
244
+                             _bbox.getLowerLeft().getY() + y * step});
209
         _sgrid.get(reqBox, &tmp);
245
         _sgrid.get(reqBox, &tmp);
210
 
246
 
211
         size_t countOk = 0;
247
         size_t countOk = 0;
@@ -223,22 +259,28 @@ void StatIdx::initIndex() {
223
         for (auto j : tmp) {
259
         for (auto j : tmp) {
224
           const auto& stat = _stations[j];
260
           const auto& stat = _stations[j];
225
           if (stat.suggestions.size() == 0 && stat.attrErrs.size() == 0) {
261
           if (stat.suggestions.size() == 0 && stat.attrErrs.size() == 0) {
226
-            if (util::geo::contains(stat.pos, reqBox)) {
227
-              countOk++;
228
-              avgXOk += stat.pos.getX();
229
-              avgYOk += stat.pos.getY();
262
+            for (const auto& pos : stat.pos) {
263
+              if (util::geo::contains(pos, reqBox)) {
264
+                countOk++;
265
+                avgXOk += pos.getX();
266
+                avgYOk += pos.getY();
267
+              }
230
             }
268
             }
231
           } else if (stat.suggestions.size() > 0 && stat.attrErrs.size() == 0) {
269
           } else if (stat.suggestions.size() > 0 && stat.attrErrs.size() == 0) {
232
-            if (util::geo::contains(stat.pos, reqBox)) {
233
-              countSugg++;
234
-              avgXSugg += stat.pos.getX();
235
-              avgYSugg += stat.pos.getY();
270
+            for (const auto& pos : stat.pos) {
271
+              if (util::geo::contains(pos, reqBox)) {
272
+                countSugg++;
273
+                avgXSugg += pos.getX();
274
+                avgYSugg += pos.getY();
275
+              }
236
             }
276
             }
237
           } else if (stat.attrErrs.size() > 0) {
277
           } else if (stat.attrErrs.size() > 0) {
238
-            if (util::geo::contains(stat.pos, reqBox)) {
239
-              countErr++;
240
-              avgXErr += stat.pos.getX();
241
-              avgYErr += stat.pos.getY();
278
+            for (const auto& pos : stat.pos) {
279
+              if (util::geo::contains(pos, reqBox)) {
280
+                countErr++;
281
+                avgXErr += pos.getX();
282
+                avgYErr += pos.getY();
283
+              }
242
             }
284
             }
243
           }
285
           }
244
         }
286
         }
@@ -248,7 +290,8 @@ void StatIdx::initIndex() {
248
           avgYOk /= countOk;
290
           avgYOk /= countOk;
249
           auto ll = util::geo::webMercToLatLng<double>(avgXOk, avgYOk);
291
           auto ll = util::geo::webMercToLatLng<double>(avgXOk, avgYOk);
250
 
292
 
251
-          _heatGridsOk[i].add({avgXOk, avgYOk}, Cluster{{avgXOk, avgYOk}, ll, countOk});
293
+          _heatGridsOk[i].add({avgXOk, avgYOk},
294
+                              Cluster{{avgXOk, avgYOk}, ll, countOk});
252
         }
295
         }
253
 
296
 
254
         if (countSugg != 0) {
297
         if (countSugg != 0) {
@@ -256,7 +299,8 @@ void StatIdx::initIndex() {
256
           avgYSugg /= countSugg;
299
           avgYSugg /= countSugg;
257
           auto ll = util::geo::webMercToLatLng<double>(avgXSugg, avgYSugg);
300
           auto ll = util::geo::webMercToLatLng<double>(avgXSugg, avgYSugg);
258
 
301
 
259
-          _heatGridsSugg[i].add({avgXSugg, avgYSugg}, Cluster{{avgXSugg, avgYSugg}, ll, countSugg});
302
+          _heatGridsSugg[i].add({avgXSugg, avgYSugg},
303
+                                Cluster{{avgXSugg, avgYSugg}, ll, countSugg});
260
         }
304
         }
261
 
305
 
262
         if (countErr != 0) {
306
         if (countErr != 0) {
@@ -264,7 +308,8 @@ void StatIdx::initIndex() {
264
           avgYErr /= countErr;
308
           avgYErr /= countErr;
265
           auto ll = util::geo::webMercToLatLng<double>(avgXErr, avgYErr);
309
           auto ll = util::geo::webMercToLatLng<double>(avgXErr, avgYErr);
266
 
310
 
267
-          _heatGridsErr[i].add({avgXErr, avgYErr}, Cluster{{avgXErr, avgYErr}, ll, countErr});
311
+          _heatGridsErr[i].add({avgXErr, avgYErr},
312
+                               Cluster{{avgXErr, avgYErr}, ll, countErr});
268
         }
313
         }
269
       }
314
       }
270
     }
315
     }
@@ -305,7 +350,7 @@ std::vector<const Group*> StatIdx::getGroups(const util::geo::DBox bbox) const {
305
   _ggrid.get(reqBox, &tmp);
350
   _ggrid.get(reqBox, &tmp);
306
 
351
 
307
   for (auto i : tmp) {
352
   for (auto i : tmp) {
308
-    // if (util::geo::intersects(_groups[i].poly, reqBox))
353
+    if (util::geo::intersects(_groups[i].poly, reqBox))
309
       ret.push_back(&_groups[i]);
354
       ret.push_back(&_groups[i]);
310
   }
355
   }
311
 
356
 
@@ -326,8 +371,10 @@ std::vector<const Station*> StatIdx::getStations(
326
   _sgrid.get(reqBox, &tmp);
371
   _sgrid.get(reqBox, &tmp);
327
 
372
 
328
   for (auto i : tmp) {
373
   for (auto i : tmp) {
329
-    if (util::geo::contains(_stations[i].pos, reqBox))
374
+    if (util::geo::intersects(util::geo::Polygon<double>(_stations[i].pos),
375
+                              reqBox)) {
330
       ret.push_back(&_stations[i]);
376
       ret.push_back(&_stations[i]);
377
+    }
331
   }
378
   }
332
 
379
 
333
   return ret;
380
   return ret;
@@ -340,8 +387,8 @@ const Station* StatIdx::getStation(size_t id) const {
340
 }
387
 }
341
 
388
 
342
 // _____________________________________________________________________________
389
 // _____________________________________________________________________________
343
-std::vector<osmfixer::Cluster> StatIdx::getHeatGridErr(const util::geo::DBox bbox,
344
-                                                    size_t z) const {
390
+std::vector<osmfixer::Cluster> StatIdx::getHeatGridErr(
391
+    const util::geo::DBox bbox, size_t z) const {
345
   auto ll = util::geo::latLngToWebMerc<double>(bbox.getLowerLeft().getX(),
392
   auto ll = util::geo::latLngToWebMerc<double>(bbox.getLowerLeft().getX(),
346
                                                bbox.getLowerLeft().getY());
393
                                                bbox.getLowerLeft().getY());
347
   auto ur = util::geo::latLngToWebMerc<double>(bbox.getUpperRight().getX(),
394
   auto ur = util::geo::latLngToWebMerc<double>(bbox.getUpperRight().getX(),
@@ -366,8 +413,8 @@ std::vector<osmfixer::Cluster> StatIdx::getHeatGridErr(const util::geo::DBox bbo
366
 }
413
 }
367
 
414
 
368
 // _____________________________________________________________________________
415
 // _____________________________________________________________________________
369
-std::vector<osmfixer::Cluster> StatIdx::getHeatGridOk(const util::geo::DBox bbox,
370
-                                                    size_t z) const {
416
+std::vector<osmfixer::Cluster> StatIdx::getHeatGridOk(
417
+    const util::geo::DBox bbox, size_t z) const {
371
   auto ll = util::geo::latLngToWebMerc<double>(bbox.getLowerLeft().getX(),
418
   auto ll = util::geo::latLngToWebMerc<double>(bbox.getLowerLeft().getX(),
372
                                                bbox.getLowerLeft().getY());
419
                                                bbox.getLowerLeft().getY());
373
   auto ur = util::geo::latLngToWebMerc<double>(bbox.getUpperRight().getX(),
420
   auto ur = util::geo::latLngToWebMerc<double>(bbox.getUpperRight().getX(),
@@ -392,8 +439,8 @@ std::vector<osmfixer::Cluster> StatIdx::getHeatGridOk(const util::geo::DBox bbox
392
 }
439
 }
393
 
440
 
394
 // _____________________________________________________________________________
441
 // _____________________________________________________________________________
395
-std::vector<osmfixer::Cluster> StatIdx::getHeatGridSugg(const util::geo::DBox bbox,
396
-                                                    size_t z) const {
442
+std::vector<osmfixer::Cluster> StatIdx::getHeatGridSugg(
443
+    const util::geo::DBox bbox, size_t z) const {
397
   auto ll = util::geo::latLngToWebMerc<double>(bbox.getLowerLeft().getX(),
444
   auto ll = util::geo::latLngToWebMerc<double>(bbox.getLowerLeft().getX(),
398
                                                bbox.getLowerLeft().getY());
445
                                                bbox.getLowerLeft().getY());
399
   auto ur = util::geo::latLngToWebMerc<double>(bbox.getUpperRight().getX(),
446
   auto ur = util::geo::latLngToWebMerc<double>(bbox.getUpperRight().getX(),
@@ -423,7 +470,8 @@ void StatIdx::initSuggestions() {
423
     if (stat.group != stat.origGroup || getGroup(stat.group)->osmid == 1) {
470
     if (stat.group != stat.origGroup || getGroup(stat.group)->osmid == 1) {
424
       Suggestion sug;
471
       Suggestion sug;
425
       sug.station = i;
472
       sug.station = i;
426
-      sug.arrow = util::geo::DLine{stat.pos, stat.pos};
473
+      auto centroid = util::geo::centroid(stat.pos);
474
+      sug.arrow = util::geo::DLine{centroid, centroid};
427
       if (getGroup(stat.origGroup)->osmid < 2) {
475
       if (getGroup(stat.origGroup)->osmid < 2) {
428
         if (getGroup(stat.group)->osmid == 1) {
476
         if (getGroup(stat.group)->osmid == 1) {
429
           // move orphan into new group
477
           // move orphan into new group
@@ -481,7 +529,7 @@ void StatIdx::initSuggestions() {
481
           sug.target_gid = stat.group;
529
           sug.target_gid = stat.group;
482
           sug.target_osm_rel_id = getGroup(stat.group)->osmid;
530
           sug.target_osm_rel_id = getGroup(stat.group)->osmid;
483
 
531
 
484
-          auto b = stat.pos;
532
+          auto b = util::geo::centroid(stat.pos);
485
           b.setX(b.getX() + 50);
533
           b.setX(b.getX() + 50);
486
           b.setY(b.getY() + 50);
534
           b.setY(b.getY() + 50);
487
 
535
 
@@ -512,8 +560,9 @@ void StatIdx::initSuggestions() {
512
 }
560
 }
513
 
561
 
514
 // _____________________________________________________________________________
562
 // _____________________________________________________________________________
515
-util::geo::DLine StatIdx::getGroupArrow(size_t stat, const util::geo::DPoint& b) const {
516
-  auto a = getStation(stat)->pos;
563
+util::geo::DLine StatIdx::getGroupArrow(size_t stat,
564
+                                        const util::geo::DPoint& b) const {
565
+  auto a = util::geo::centroid(getStation(stat)->pos);
517
 
566
 
518
   auto pl = util::geo::PolyLine<double>(a, b);
567
   auto pl = util::geo::PolyLine<double>(a, b);
519
   auto bb = pl.getPointAtDist(fmax(pl.getLength() / 2, pl.getLength() - 5)).p;
568
   auto bb = pl.getPointAtDist(fmax(pl.getLength() / 2, pl.getLength() - 5)).p;
@@ -528,7 +577,6 @@ util::geo::DLine StatIdx::getGroupArrow(size_t stat, const util::geo::DPoint& b)
528
   if (util::geo::dist(a, bb) < 20) {
577
   if (util::geo::dist(a, bb) < 20) {
529
     line = {a, bb};
578
     line = {a, bb};
530
   } else {
579
   } else {
531
-
532
     int side = rand() % 2;
580
     int side = rand() % 2;
533
     if (!side) side = -1;
581
     if (!side) side = -1;
534
 
582
 
@@ -536,9 +584,9 @@ util::geo::DLine StatIdx::getGroupArrow(size_t stat, const util::geo::DPoint& b)
536
     auto rot2 = util::geo::rotate(util::geo::DLine{a, bb}, -20 * side, bb);
584
     auto rot2 = util::geo::rotate(util::geo::DLine{a, bb}, -20 * side, bb);
537
     auto rot3 = util::geo::rotate(util::geo::DLine{a, bb}, -120 * side, bb);
585
     auto rot3 = util::geo::rotate(util::geo::DLine{a, bb}, -120 * side, bb);
538
 
586
 
539
-    auto i =
540
-        util::geo::intersection(util::geo::LineSegment<double>{rot1[0], rot1[1]},
541
-                                util::geo::LineSegment<double>{rot2[0], rot2[1]});
587
+    auto i = util::geo::intersection(
588
+        util::geo::LineSegment<double>{rot1[0], rot1[1]},
589
+        util::geo::LineSegment<double>{rot2[0], rot2[1]});
542
 
590
 
543
     line =
591
     line =
544
         util::geo::BezierCurve<double>(a, i, rot3[0], bb).render(5).getLine();
592
         util::geo::BezierCurve<double>(a, i, rot3[0], bb).render(5).getLine();

+ 19 - 12
src/osmfixer/index/StatIdx.h

@@ -19,23 +19,26 @@ struct AttrErr {
19
   std::string attr;
19
   std::string attr;
20
   std::string otherAttr;
20
   std::string otherAttr;
21
   double conf;
21
   double conf;
22
-  size_t otherId;
22
+  int64_t otherId;
23
 };
23
 };
24
 
24
 
25
 struct Suggestion {
25
 struct Suggestion {
26
   uint16_t type;
26
   uint16_t type;
27
   size_t station;
27
   size_t station;
28
-  size_t target_gid, orig_gid, target_osm_rel_id, orig_osm_rel_id;
28
+  // if negative, it is a way!
29
+  int64_t target_gid, orig_gid, target_osm_rel_id, orig_osm_rel_id;
29
   std::string attrErrName;
30
   std::string attrErrName;
30
   util::geo::DLine arrow;
31
   util::geo::DLine arrow;
31
 };
32
 };
32
 
33
 
33
 struct Station {
34
 struct Station {
34
   size_t id;
35
   size_t id;
35
-  size_t osmid;
36
+  // if negative, it is a way!
37
+  int64_t osmid;
36
   size_t origGroup, group;
38
   size_t origGroup, group;
37
-  util::geo::DPoint pos;
38
-  util::geo::DPoint latLng;
39
+  util::geo::DPoint centroid;
40
+  util::geo::DLine pos;
41
+  util::geo::DLine latLng;
39
   OsmAttrs attrs;
42
   OsmAttrs attrs;
40
   std::vector<AttrErr> attrErrs;
43
   std::vector<AttrErr> attrErrs;
41
   std::vector<size_t> suggestions;
44
   std::vector<size_t> suggestions;
@@ -43,8 +46,10 @@ struct Station {
43
 
46
 
44
 struct Group {
47
 struct Group {
45
   size_t id;
48
   size_t id;
46
-  size_t osmid;
49
+  int64_t osmid;
47
   util::geo::DPolygon poly;
50
   util::geo::DPolygon poly;
51
+  util::geo::DPolygon llPoly;
52
+  util::geo::DPoint centroid;
48
   OsmAttrs attrs;
53
   OsmAttrs attrs;
49
   std::vector<size_t> stations;
54
   std::vector<size_t> stations;
50
   std::vector<size_t> polyStations;
55
   std::vector<size_t> polyStations;
@@ -77,16 +82,17 @@ class StatIdx {
77
   const Suggestion* getSuggestion(size_t id) const;
82
   const Suggestion* getSuggestion(size_t id) const;
78
 
83
 
79
   std::vector<Cluster> getHeatGridOk(const util::geo::DBox bbox,
84
   std::vector<Cluster> getHeatGridOk(const util::geo::DBox bbox,
80
-      size_t z) const;
85
+                                     size_t z) const;
81
 
86
 
82
   std::vector<Cluster> getHeatGridSugg(const util::geo::DBox bbox,
87
   std::vector<Cluster> getHeatGridSugg(const util::geo::DBox bbox,
83
-      size_t z) const;
88
+                                       size_t z) const;
84
 
89
 
85
   std::vector<Cluster> getHeatGridErr(const util::geo::DBox bbox,
90
   std::vector<Cluster> getHeatGridErr(const util::geo::DBox bbox,
86
-      size_t z) const;
91
+                                      size_t z) const;
87
 
92
 
88
  private:
93
  private:
89
-  void addStation(size_t id, double lat, double lng, size_t origGroup,
94
+  void addStation(int64_t osmid, const util::geo::DLine& geom,
95
+                  const util::geo::DLine& latLngs, size_t origGroup,
90
                   size_t group, const OsmAttrs& attrs);
96
                   size_t group, const OsmAttrs& attrs);
91
   void addGroup(size_t id, const OsmAttrs& attrs);
97
   void addGroup(size_t id, const OsmAttrs& attrs);
92
 
98
 
@@ -101,12 +107,13 @@ class StatIdx {
101
   std::vector<Suggestion> _suggestions;
107
   std::vector<Suggestion> _suggestions;
102
   util::geo::DBox _bbox;
108
   util::geo::DBox _bbox;
103
 
109
 
104
-  util::geo::Grid<size_t, util::geo::Point, double> _sgrid;
110
+  util::geo::Grid<size_t, util::geo::Polygon, double> _sgrid;
105
   util::geo::Grid<size_t, util::geo::Polygon, double> _ggrid;
111
   util::geo::Grid<size_t, util::geo::Polygon, double> _ggrid;
106
   util::geo::Grid<size_t, util::geo::Line, double> _suggrid;
112
   util::geo::Grid<size_t, util::geo::Line, double> _suggrid;
107
 
113
 
108
   std::vector<util::geo::Grid<Cluster, util::geo::Point, double>> _heatGridsOk;
114
   std::vector<util::geo::Grid<Cluster, util::geo::Point, double>> _heatGridsOk;
109
-  std::vector<util::geo::Grid<Cluster, util::geo::Point, double>> _heatGridsSugg;
115
+  std::vector<util::geo::Grid<Cluster, util::geo::Point, double>>
116
+      _heatGridsSugg;
110
   std::vector<util::geo::Grid<Cluster, util::geo::Point, double>> _heatGridsErr;
117
   std::vector<util::geo::Grid<Cluster, util::geo::Point, double>> _heatGridsErr;
111
 };
118
 };
112
 }  // namespace osmfixer
119
 }  // namespace osmfixer

+ 81 - 55
src/osmfixer/server/StatServer.cpp

@@ -84,8 +84,8 @@ util::http::Answer StatServer::handleHeatMapReq(const Params& pars) const {
84
   std::stringstream json;
84
   std::stringstream json;
85
 
85
 
86
   size_t p = 5;
86
   size_t p = 5;
87
-  if (z < 11) p = 4;
88
-  if (z < 10) p = 3;
87
+  if (z < 11) p = 3;
88
+  if (z < 10) p = 2;
89
   if (z < 9) p = 2;
89
   if (z < 9) p = 2;
90
   if (z < 7) p = 1;
90
   if (z < 7) p = 1;
91
 
91
 
@@ -98,8 +98,8 @@ util::http::Answer StatServer::handleHeatMapReq(const Params& pars) const {
98
   for (const auto& idxPair : _idxs) {
98
   for (const auto& idxPair : _idxs) {
99
     const auto& idx = idxPair.second;
99
     const auto& idx = idxPair.second;
100
 
100
 
101
-    auto hgOk = idx.getHeatGridOk(bbox, z);
102
-    for (auto cluster : hgOk) {
101
+    const auto& hgOk = idx.getHeatGridOk(bbox, z);
102
+    for (const auto& cluster : hgOk) {
103
       json << sep;
103
       json << sep;
104
       sep = ',';
104
       sep = ',';
105
 
105
 
@@ -114,9 +114,9 @@ util::http::Answer StatServer::handleHeatMapReq(const Params& pars) const {
114
   for (const auto& idxPair : _idxs) {
114
   for (const auto& idxPair : _idxs) {
115
     const auto& idx = idxPair.second;
115
     const auto& idx = idxPair.second;
116
 
116
 
117
-    auto hgSugg = idx.getHeatGridSugg(bbox, z);
117
+    const auto& hgSugg = idx.getHeatGridSugg(bbox, z);
118
 
118
 
119
-    for (auto cluster : hgSugg) {
119
+    for (const auto& cluster : hgSugg) {
120
       json << sep;
120
       json << sep;
121
       sep = ',';
121
       sep = ',';
122
 
122
 
@@ -131,8 +131,8 @@ util::http::Answer StatServer::handleHeatMapReq(const Params& pars) const {
131
   for (const auto& idxPair : _idxs) {
131
   for (const auto& idxPair : _idxs) {
132
     const auto& idx = idxPair.second;
132
     const auto& idx = idxPair.second;
133
 
133
 
134
-    auto hgErr = idx.getHeatGridErr(bbox, z);
135
-    for (auto cluster : hgErr) {
134
+    const auto& hgErr = idx.getHeatGridErr(bbox, z);
135
+    for (const auto& cluster : hgErr) {
136
       json << sep;
136
       json << sep;
137
       sep = ',';
137
       sep = ',';
138
 
138
 
@@ -159,6 +159,9 @@ util::http::Answer StatServer::handleMapReq(const Params& pars) const {
159
   if (pars.count("cb")) cb = pars.find("cb")->second.c_str();
159
   if (pars.count("cb")) cb = pars.find("cb")->second.c_str();
160
   auto box = util::split(pars.find("bbox")->second, ',');
160
   auto box = util::split(pars.find("bbox")->second, ',');
161
 
161
 
162
+  double z = 0;
163
+  if (pars.count("z")) z = atoi(pars.find("z")->second.c_str());
164
+
162
   if (box.size() != 4) throw std::invalid_argument("Invalid request.");
165
   if (box.size() != 4) throw std::invalid_argument("Invalid request.");
163
 
166
 
164
   double lat1 = atof(box[0].c_str());
167
   double lat1 = atof(box[0].c_str());
@@ -169,9 +172,19 @@ util::http::Answer StatServer::handleMapReq(const Params& pars) const {
169
   util::geo::DBox bbox(util::geo::DPoint(lat1, lng1),
172
   util::geo::DBox bbox(util::geo::DPoint(lat1, lng1),
170
                        util::geo::DPoint(lat2, lng2));
173
                        util::geo::DPoint(lat2, lng2));
171
 
174
 
175
+  size_t p = 7;
176
+  if (z < 20) p = 6;
177
+  if (z < 17) p = 5;
178
+  if (z < 15) p = 4;
179
+  if (z < 13) p = 3;
180
+  if (z < 10) p = 2;
181
+  if (z < 9) p = 2;
182
+  if (z < 7) p = 1;
183
+
184
+
172
   std::stringstream json;
185
   std::stringstream json;
173
 
186
 
174
-  json << std::fixed << std::setprecision(6);
187
+  json << std::fixed << std::setprecision(p);
175
 
188
 
176
   if (cb.size()) json << cb << "(";
189
   if (cb.size()) json << cb << "(";
177
   json << "{\"stats\":[";
190
   json << "{\"stats\":[";
@@ -180,11 +193,11 @@ util::http::Answer StatServer::handleMapReq(const Params& pars) const {
180
   for (size_t did = 0; did < _idxs.size(); did++) {
193
   for (size_t did = 0; did < _idxs.size(); did++) {
181
     const auto& idx = _idxs[did].second;
194
     const auto& idx = _idxs[did].second;
182
 
195
 
183
-    auto ret = idx.getStations(bbox);
184
-    for (auto stat : ret) {
196
+    const auto& ret = idx.getStations(bbox);
197
+    for (const auto& stat : ret) {
185
       json << sep;
198
       json << sep;
186
       sep = ',';
199
       sep = ',';
187
-      printStation(stat, did, &json);
200
+      printStation(stat, did, z < 15, &json);
188
     }
201
     }
189
   }
202
   }
190
 
203
 
@@ -193,25 +206,27 @@ util::http::Answer StatServer::handleMapReq(const Params& pars) const {
193
   for (size_t did = 0; did < _idxs.size(); did++) {
206
   for (size_t did = 0; did < _idxs.size(); did++) {
194
     const auto& idx = _idxs[did].second;
207
     const auto& idx = _idxs[did].second;
195
 
208
 
196
-    auto gret = idx.getGroups(bbox);
197
-    for (auto group : gret) {
209
+    const auto& gret = idx.getGroups(bbox);
210
+    for (const auto& group : gret) {
198
       if (group->polyStations.size() == 1 && group->osmid < 2) continue;
211
       if (group->polyStations.size() == 1 && group->osmid < 2) continue;
199
       json << sep;
212
       json << sep;
200
       sep = ',';
213
       sep = ',';
201
-      printGroup(group, did, &json);
214
+      printGroup(group, did, z < 15, &json);
202
     }
215
     }
203
   }
216
   }
204
 
217
 
205
   json << "], \"su\":[";
218
   json << "], \"su\":[";
206
   sep = ' ';
219
   sep = ' ';
207
-  for (size_t did = 0; did < _idxs.size(); did++) {
208
-    const auto& idx = _idxs[did].second;
209
-
210
-    auto suggs = idx.getSuggestions(bbox);
211
-    for (auto sugg : suggs) {
212
-      json << sep;
213
-      sep = ',';
214
-      printSugg(sugg, did, &json);
220
+  if (z > 15) {
221
+    for (size_t did = 0; did < _idxs.size(); did++) {
222
+      const auto& idx = _idxs[did].second;
223
+
224
+      const auto& suggs = idx.getSuggestions(bbox);
225
+      for (const auto& sugg : suggs) {
226
+        json << sep;
227
+        sep = ',';
228
+        printSugg(sugg, did, &json);
229
+      }
215
     }
230
     }
216
   }
231
   }
217
 
232
 
@@ -226,21 +241,32 @@ util::http::Answer StatServer::handleMapReq(const Params& pars) const {
226
 }
241
 }
227
 
242
 
228
 // _____________________________________________________________________________
243
 // _____________________________________________________________________________
229
-void StatServer::printStation(const Station* stat, size_t did,
244
+void StatServer::printStation(const Station* stat, size_t did, bool simple,
230
                               std::ostream* out) const {
245
                               std::ostream* out) const {
231
   const auto& idx = _idxs[did].second;
246
   const auto& idx = _idxs[did].second;
232
   const auto& range = _idxs[did].first;
247
   const auto& range = _idxs[did].first;
233
 
248
 
234
-  auto latLng =
235
-      util::geo::webMercToLatLng<double>(stat->pos.getX(), stat->pos.getY());
236
-  (*out) << "{\"id\":" << stat->id + range.sidStart
237
-         << ",\"lat\":" << latLng.getY() << ",\"lon\":" << latLng.getX();
249
+  (*out) << "{\"i\":" << stat->id + range.sidStart << ",\"g\":[";
250
+
251
+  std::string sep = "";
252
+
253
+  if (simple) {
254
+      (*out) << sep << "[" << stat->centroid.getY() << "," << stat->centroid.getX() << "]";
255
+  } else {
256
+    for (const auto& ll : stat->latLng) {
257
+      (*out) << sep << "[" << ll.getY() << "," << ll.getX() << "]";
258
+      sep = ",";
259
+    }
260
+  }
261
+
262
+  (*out) << "]";
238
 
263
 
239
   if (stat->origGroup != stat->group || idx.getGroup(stat->group)->osmid == 1)
264
   if (stat->origGroup != stat->group || idx.getGroup(stat->group)->osmid == 1)
240
-    (*out) << ",\"su\":1";
265
+    (*out) << ",\"s\":1";
241
 
266
 
242
   if (stat->attrErrs.size())
267
   if (stat->attrErrs.size())
243
-    (*out) << ",\"attrerrs\":" << stat->attrErrs.size();
268
+    (*out) << ",\"e\":" << stat->attrErrs.size();
269
+
244
   (*out) << "}";
270
   (*out) << "}";
245
 }
271
 }
246
 
272
 
@@ -251,14 +277,14 @@ void StatServer::printSugg(const Suggestion* sugg, size_t did,
251
 
277
 
252
   std::vector<util::geo::DPoint> projArrow;
278
   std::vector<util::geo::DPoint> projArrow;
253
 
279
 
254
-  for (auto p : sugg->arrow) {
280
+  for (const auto& p : sugg->arrow) {
255
     projArrow.push_back(util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
281
     projArrow.push_back(util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
256
   }
282
   }
257
 
283
 
258
-  (*out) << "{\"id\":" << sugg->station + range.suggIdStart
259
-         << ",\"type\":" << sugg->type << ",\"arrow\":[";
284
+  (*out) << "{\"i\":" << sugg->station + range.suggIdStart
285
+         << ",\"t\":" << sugg->type << ",\"a\":[";
260
   char sep = ' ';
286
   char sep = ' ';
261
-  for (auto p : projArrow) {
287
+  for (const auto& p : projArrow) {
262
     (*out) << sep << "[" << p.getY() << "," << p.getX() << "]";
288
     (*out) << sep << "[" << p.getY() << "," << p.getX() << "]";
263
     sep = ',';
289
     sep = ',';
264
   }
290
   }
@@ -267,24 +293,22 @@ void StatServer::printSugg(const Suggestion* sugg, size_t did,
267
 }
293
 }
268
 
294
 
269
 // _____________________________________________________________________________
295
 // _____________________________________________________________________________
270
-void StatServer::printGroup(const Group* group, size_t did,
296
+void StatServer::printGroup(const Group* group, size_t did, bool simple,
271
                             std::ostream* out) const {
297
                             std::ostream* out) const {
272
   const auto& range = _idxs[did].first;
298
   const auto& range = _idxs[did].first;
273
 
299
 
274
-  std::vector<util::geo::DPoint> projPoly;
275
-
276
-  for (auto p : group->poly.getOuter()) {
277
-    projPoly.push_back(util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
278
-  }
279
-
280
-  (*out) << "{\"id\":" << group->id + range.gidStart << ",\"poly\":[";
300
+  (*out) << "{\"i\":" << group->id + range.gidStart << ",\"g\":[";
281
   char sep = ' ';
301
   char sep = ' ';
282
-  for (auto p : projPoly) {
283
-    (*out) << sep << "[" << p.getY() << "," << p.getX() << "]";
284
-    sep = ',';
302
+  if (simple) {
303
+    (*out) << sep << "[" << group->centroid.getY() << "," << group->centroid.getX() << "]";
304
+  } else {
305
+    for (const auto& p : group->llPoly.getOuter()) {
306
+      (*out) << sep << "[" << p.getY() << "," << p.getX() << "]";
307
+      sep = ',';
308
+    }
285
   }
309
   }
286
   (*out) << "]";
310
   (*out) << "]";
287
-  if (group->osmid == 1) (*out) << ",\"new\":1";
311
+  if (group->osmid == 1) (*out) << ",\"n\":1";
288
   (*out) << "}";
312
   (*out) << "}";
289
 }
313
 }
290
 
314
 
@@ -295,7 +319,7 @@ std::string StatServer::parseUrl(std::string u, std::string pl,
295
 
319
 
296
   if (parts.size() > 1) {
320
   if (parts.size() > 1) {
297
     auto kvs = util::split(parts[1], '&');
321
     auto kvs = util::split(parts[1], '&');
298
-    for (auto kv : kvs) {
322
+    for (const auto& kv : kvs) {
299
       auto kvp = util::split(kv, '=');
323
       auto kvp = util::split(kv, '=');
300
       if (kvp.size() == 1) kvp.push_back("");
324
       if (kvp.size() == 1) kvp.push_back("");
301
       (*params)[util::urlDecode(kvp[0])] = kvp[1];
325
       (*params)[util::urlDecode(kvp[0])] = kvp[1];
@@ -304,7 +328,7 @@ std::string StatServer::parseUrl(std::string u, std::string pl,
304
 
328
 
305
   // also parse post data
329
   // also parse post data
306
   auto kvs = util::split(pl, '&');
330
   auto kvs = util::split(pl, '&');
307
-  for (auto kv : kvs) {
331
+  for (const auto& kv : kvs) {
308
     auto kvp = util::split(kv, '=');
332
     auto kvp = util::split(kv, '=');
309
     if (kvp.size() == 1) kvp.push_back("");
333
     if (kvp.size() == 1) kvp.push_back("");
310
     (*params)[util::urlDecode(kvp[0])] = kvp[1];
334
     (*params)[util::urlDecode(kvp[0])] = kvp[1];
@@ -358,7 +382,7 @@ util::http::Answer StatServer::handleGroupReq(const Params& pars) const {
358
   const auto& idx = _idxs[did].second;
382
   const auto& idx = _idxs[did].second;
359
   size_t idxGid = gid - range.gidStart;
383
   size_t idxGid = gid - range.gidStart;
360
 
384
 
361
-  auto group = idx.getGroup(idxGid);
385
+  const auto& group = idx.getGroup(idxGid);
362
 
386
 
363
   if (!group) return util::http::Answer("404 Not Found", "Group not found.");
387
   if (!group) return util::http::Answer("404 Not Found", "Group not found.");
364
 
388
 
@@ -393,7 +417,7 @@ util::http::Answer StatServer::handleGroupReq(const Params& pars) const {
393
   for (const auto& sid : group->stations) {
417
   for (const auto& sid : group->stations) {
394
     json << sep;
418
     json << sep;
395
     sep = ',';
419
     sep = ',';
396
-    const auto stat = idx.getStation(sid);
420
+    const auto& stat = idx.getStation(sid);
397
     json << "{\"id\":" << sid + range.sidStart << ","
421
     json << "{\"id\":" << sid + range.sidStart << ","
398
          << "\"osmid\":" << stat->osmid << ","
422
          << "\"osmid\":" << stat->osmid << ","
399
          << "\"group\":" << stat->group + range.gidStart << ","
423
          << "\"group\":" << stat->group + range.gidStart << ","
@@ -445,7 +469,7 @@ util::http::Answer StatServer::handleStatReq(const Params& pars) const {
445
   const auto& idx = _idxs[did].second;
469
   const auto& idx = _idxs[did].second;
446
   size_t idxSid = sid - range.sidStart;
470
   size_t idxSid = sid - range.sidStart;
447
 
471
 
448
-  auto stat = idx.getStation(idxSid);
472
+  const auto& stat = idx.getStation(idxSid);
449
 
473
 
450
   if (!stat) return util::http::Answer("404 Not Found", "Station not found.");
474
   if (!stat) return util::http::Answer("404 Not Found", "Station not found.");
451
 
475
 
@@ -453,11 +477,13 @@ util::http::Answer StatServer::handleStatReq(const Params& pars) const {
453
 
477
 
454
   json << std::fixed << std::setprecision(6);
478
   json << std::fixed << std::setprecision(6);
455
 
479
 
480
+  auto centroid = util::geo::centroid(stat->latLng);
481
+
456
   if (cb.size()) json << cb << "(";
482
   if (cb.size()) json << cb << "(";
457
   json << "{\"id\":" << sid << ","
483
   json << "{\"id\":" << sid << ","
458
        << "\"osmid\":" << stat->osmid << ","
484
        << "\"osmid\":" << stat->osmid << ","
459
-       << "\"lat\":" << stat->latLng.getY() << ","
460
-       << "\"lon\":" << stat->latLng.getX() << ","
485
+       << "\"lat\":" << centroid.getY() << ","
486
+       << "\"lon\":" << centroid.getX() << ","
461
        << "\"attrs\":{";
487
        << "\"attrs\":{";
462
 
488
 
463
   char sep = ' ';
489
   char sep = ' ';
@@ -482,7 +508,7 @@ util::http::Answer StatServer::handleStatReq(const Params& pars) const {
482
   for (const auto& err : stat->attrErrs) {
508
   for (const auto& err : stat->attrErrs) {
483
     json << sep;
509
     json << sep;
484
     sep = ',';
510
     sep = ',';
485
-    const auto otherStat = idx.getStation(err.otherId);
511
+    const auto& otherStat = idx.getStation(err.otherId);
486
     json << "{";
512
     json << "{";
487
     json << "\"attr\":[\"" << err.attr << "\",\"" << stat->attrs.at(err.attr)[0]
513
     json << "\"attr\":[\"" << err.attr << "\",\"" << stat->attrs.at(err.attr)[0]
488
          << "\"]";
514
          << "\"]";
@@ -537,7 +563,7 @@ util::http::Answer StatServer::handleStatReq(const Params& pars) const {
537
     }
563
     }
538
   }
564
   }
539
 
565
 
540
-  for (auto attrErr : stat->attrErrs) {
566
+  for (const auto& attrErr : stat->attrErrs) {
541
     const auto otherStat = idx.getStation(attrErr.otherId);
567
     const auto otherStat = idx.getStation(attrErr.otherId);
542
     if (otherStat->osmid == stat->osmid) {
568
     if (otherStat->osmid == stat->osmid) {
543
       // fix attributes
569
       // fix attributes

+ 2 - 2
src/osmfixer/server/StatServer.h

@@ -35,8 +35,8 @@ class StatServer : public util::http::Handler {
35
   util::http::Answer handleStatReq(const Params& pars) const;
35
   util::http::Answer handleStatReq(const Params& pars) const;
36
   util::http::Answer handleGroupReq(const Params& pars) const;
36
   util::http::Answer handleGroupReq(const Params& pars) const;
37
 
37
 
38
-  void printStation(const Station* stat, size_t did, std::ostream* out) const;
39
-  void printGroup(const Group* stat, size_t did, std::ostream* out) const;
38
+  void printStation(const Station* stat, size_t did, bool simple, std::ostream* out) const;
39
+  void printGroup(const Group* stat, size_t did, bool simple, std::ostream* out) const;
40
   void printSugg(const Suggestion* stat, size_t did, std::ostream* out) const;
40
   void printSugg(const Suggestion* stat, size_t did, std::ostream* out) const;
41
 
41
 
42
   size_t getDidBySid(size_t sid) const;
42
   size_t getDidBySid(size_t sid) const;

File diff suppressed because it is too large
+ 13 - 9
web/index.html


+ 80 - 48
web/script.js

@@ -1,31 +1,43 @@
1
-widths = [12, 13, 13, 13, 13, 13, 13, 13];
2
-opas = [0.8, 0.6, 0.5, 0.5, 0.4];
3
-mwidths = [1, 1, 1, 1.5, 2, 3, 5, 6, 6, 4, 3, 2];
1
+var widths = [12, 13, 13, 13, 13, 13, 13, 13];
2
+var opas = [0.8, 0.6, 0.5, 0.5, 0.4];
3
+var mwidths = [1, 1, 1, 1.5, 2, 3, 5, 6, 6, 4, 3, 2];
4
 
4
 
5
-backend = "";
5
+var backend = "";
6
+var openGr = -1;
7
+var openSt = -1;
6
 
8
 
7
 function marker(stat, z) {
9
 function marker(stat, z) {
8
-    if (z > 15) {
9
-        return L.circle(
10
-            [stat.lat, stat.lon], {
11
-                color: 'black',
12
-                fillColor: stat.attrerrs ? 'red' : stat.su ? '#0000c3' : '#78f378',
13
-                radius: mwidths[23 - z],
14
-                fillOpacity: 1,
15
-                weight: 1,
16
-                id: stat.id
17
-            }
18
-        );
10
+    if (stat.g.length == 1) {
11
+        if (z > 15) {
12
+            return L.circle(
13
+                stat.g[0], {
14
+                    color: 'black',
15
+                    fillColor: stat.e ? 'red' : stat.s ? '#0000c3' : '#78f378',
16
+                    radius: mwidths[23 - z],
17
+                    fillOpacity: 1,
18
+                    weight: z > 17 ? 1.5 : 1,
19
+                    id: stat.i
20
+                }
21
+            );
22
+        } else {
23
+            return L.polyline(
24
+                [stat.g[0], stat.g[0]], {
25
+                    color: stat.e ? 'red' : stat.s ? '#0000c3' : '#78f378',
26
+                    weight: widths[15 - z],
27
+                    opacity: opas[15 - z],
28
+                    id: stat.i
29
+                }
30
+            );
31
+        }
19
     } else {
32
     } else {
20
-        return L.polyline(
21
-            [
22
-                [stat.lat, stat.lon],
23
-                [stat.lat, stat.lon]
24
-            ], {
25
-                color: stat.attrerrs ? 'red' : stat.su ? '#0000c3' : '#78f378',
26
-                weight: widths[15 - z],
27
-                opacity: opas[15 - z],
28
-                id: stat.id
33
+        return L.polygon(
34
+            stat.g, {
35
+                color: z > 15 ? 'black': (stat.e ? 'red' : stat.s ? '#0000c3' : '#78f378'),
36
+                fillColor: stat.e ? 'red' : stat.s ? '#0000c3' : '#78f378',
37
+                smoothFactor: 0,
38
+                fillOpacity: 0.75,
39
+                weight: z > 17 ? 1.5 : 1,
40
+                id: stat.i
29
             }
41
             }
30
         );
42
         );
31
     }
43
     }
@@ -34,24 +46,26 @@ function marker(stat, z) {
34
 function poly(group, z) {
46
 function poly(group, z) {
35
     var style = {
47
     var style = {
36
         color: "#85f385",
48
         color: "#85f385",
49
+        fillColor: "#85f385",
37
         smoothFactor: 0.4,
50
         smoothFactor: 0.4,
38
         fillOpacity: 0.2,
51
         fillOpacity: 0.2,
39
-        id: group.id
52
+        id: group.i
40
     };
53
     };
41
     if (group.new) {
54
     if (group.new) {
42
         style.color = "#0000c3";
55
         style.color = "#0000c3";
56
+        style.fillColor = "#0000c3";
43
     }
57
     }
44
     if (z < 16) {
58
     if (z < 16) {
45
         style.weight = 11;
59
         style.weight = 11;
46
         style.opacity = 0.5;
60
         style.opacity = 0.5;
47
         style.fillOpacity = 0.5;
61
         style.fillOpacity = 0.5;
48
     }
62
     }
49
-    return L.polygon(group.poly, style)
63
+    return L.polygon(group.g, style)
50
 }
64
 }
51
 
65
 
52
 function sugArr(sug, z) {
66
 function sugArr(sug, z) {
53
-    return L.polyline(sug.arrow, {
54
-        id: sug.id,
67
+    return L.polyline(sug.a, {
68
+        id: sug.i,
55
         color: '#0000c3',
69
         color: '#0000c3',
56
         smoothFactor: 0.1,
70
         smoothFactor: 0.1,
57
         weight: 4,
71
         weight: 4,
@@ -60,12 +74,18 @@ function sugArr(sug, z) {
60
 }
74
 }
61
 
75
 
62
 function renderStat(stat) {
76
 function renderStat(stat) {
77
+    openSt = stat.id;
78
+    nodeHl(stat.id);
63
     var attrrows = {};
79
     var attrrows = {};
64
     var ll = {
80
     var ll = {
65
         lat: stat.lat,
81
         lat: stat.lat,
66
         lon: stat.lon
82
         lon: stat.lon
67
     };
83
     };
68
 
84
 
85
+    var way = stat.osmid < 0;
86
+    var osmid = Math.abs(stat.osmid);
87
+    var ident = way ? "Way" : "Node";
88
+
69
     var content = document.createElement('div');
89
     var content = document.createElement('div');
70
     content.setAttribute("id", "nav")
90
     content.setAttribute("id", "nav")
71
     var attrTbl = document.createElement('table');
91
     var attrTbl = document.createElement('table');
@@ -73,12 +93,14 @@ function renderStat(stat) {
73
     var suggD = document.createElement('div');
93
     var suggD = document.createElement('div');
74
     suggD.setAttribute("id", "sugg")
94
     suggD.setAttribute("id", "sugg")
75
 
95
 
96
+    content.innerHTML = ident + " <a target='_blank' href='https://www.openstreetmap.org/"+ident.toLowerCase()+"/" + osmid + "'>" + osmid + "</a>";
97
+
76
     if (stat.attrs.name) {
98
     if (stat.attrs.name) {
77
-        content.innerHTML = "Node <a onmouseover='nodeHl( " + stat.id + ")' onmouseout='nodeUnHl( " + stat.id + ")' target='_blank' href='https://www.openstreetmap.org/node/" + stat.osmid + "'>" + stat.osmid + "</a> (<b>\"" + stat.attrs.name + "\"</b>)";
78
-    } else {
79
-        content.innerHTML = "Node <a onmouseover='nodeHl( " + stat.id + ")' onmouseout='nodeUnHl( " + stat.id + ")' target='_blank' href='https://www.openstreetmap.org/node/" + stat.osmid + "'>" + stat.osmid + "</a>";
99
+        content.innerHTML += " (<b>\"" + stat.attrs.name + "\"</b>)";
80
     }
100
     }
81
 
101
 
102
+    content.innerHTML += "<a class='editbut' target='_blank' href='https://www.openstreetmap.org/edit?" + ident.toLowerCase() + "=" + osmid +"'>&#9998;</a>";
103
+
82
     content.appendChild(attrTbl);
104
     content.appendChild(attrTbl);
83
     content.appendChild(suggD);
105
     content.appendChild(suggD);
84
 
106
 
@@ -106,7 +128,8 @@ function renderStat(stat) {
106
         var info = document.createElement('div');
128
         var info = document.createElement('div');
107
 
129
 
108
         if (err.other_osmid != stat.osmid) {
130
         if (err.other_osmid != stat.osmid) {
109
-            info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> in node <a onmouseover='nodeHl( " + err.other + ")' onmouseout='nodeUnHl( " + err.other + ")' target=\"_blank\" href=\"https://www.openstreetmap.org/node/" + err.other_osmid + "\">" + err.other_osmid + "</a>";
131
+            var ident = stat.osmid < 0 ? "way" : "node";
132
+            info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> in " + ident + " <a onmouseover='nodeHl( " + err.other + ")' onmouseout='nodeUnHl( " + err.other + ")' target=\"_blank\" href=\"https://www.openstreetmap.org/"+ident+"/" + Math.abs(err.other_osmid) + "\">" + Math.abs(err.other_osmid) + "</a>";
110
         } else {
133
         } else {
111
             info.innerHTML = "Does not match '" + err.other_attr[0] + "' = '" + err.other_attr[1];
134
             info.innerHTML = "Does not match '" + err.other_attr[0] + "' = '" + err.other_attr[1];
112
         }
135
         }
@@ -158,7 +181,8 @@ function renderStat(stat) {
158
         })
181
         })
159
         .setLatLng(ll)
182
         .setLatLng(ll)
160
         .setContent(content)
183
         .setContent(content)
161
-        .openOn(map);
184
+        .openOn(map)
185
+        .on('remove', function() {openSt = -1; nodeUnHl(stat.id);;});
162
 }
186
 }
163
 
187
 
164
 function openStat(id) {
188
 function openStat(id) {
@@ -175,7 +199,9 @@ function openStat(id) {
175
 }
199
 }
176
 
200
 
177
 function renderGroup(grp, ll) {
201
 function renderGroup(grp, ll) {
202
+    openGr = grp.id;
178
     var attrrows = {};
203
     var attrrows = {};
204
+    groupHl(grp.id);
179
 
205
 
180
     var content = document.createElement('div');
206
     var content = document.createElement('div');
181
     content.setAttribute("id", "nav");
207
     content.setAttribute("id", "nav");
@@ -189,9 +215,10 @@ function renderGroup(grp, ll) {
189
     oldMembers.innerHTML = "<span class='oldmemberstit'>Existing Members</span>";
215
     oldMembers.innerHTML = "<span class='oldmemberstit'>Existing Members</span>";
190
 
216
 
191
     if (grp.osmid == 1) {
217
     if (grp.osmid == 1) {
192
-        content.innerHTML = "<span class='grouplink' onmouseover='groupHl( " + grp.id + ")' onmouseout='groupUnHl( " + grp.id + ")'>New relation</span> <tt>public_transport=stop_area</tt>";
218
+        content.innerHTML = "<span class='grouplink'>New relation</span> <tt>public_transport=stop_area</tt>";
193
     } else {
219
     } else {
194
-        content.innerHTML = "OSM relation <a onmouseover='groupHl( " + grp.id + ")' onmouseout='groupUnHl( " + grp.id + ")' target='_blank' href='https://www.openstreetmap.org/relation/" + grp.osmid + "'>" + grp.osmid + "</a>";
220
+        content.innerHTML = "OSM relation <a target='_blank' href='https://www.openstreetmap.org/relation/" + grp.osmid + "'>" + grp.osmid + "</a>";
221
+        content.innerHTML += "<a class='editbut' target='_blank' href='https://www.openstreetmap.org/edit?relation=" + grp.osmid +"'>&#9998;</a>";
195
     }
222
     }
196
 
223
 
197
     content.appendChild(newMembers);
224
     content.appendChild(newMembers);
@@ -200,11 +227,12 @@ function renderGroup(grp, ll) {
200
     for (var key in grp.stations) {
227
     for (var key in grp.stations) {
201
         var stat = grp.stations[key];
228
         var stat = grp.stations[key];
202
         var row = document.createElement('div');
229
         var row = document.createElement('div');
230
+        var ident = stat.osmid < 0 ? "Way" : "Node";
231
+
232
+        row.innerHTML = ident + " <a onmouseover='nodeHl( " + stat.id + ")' onmouseout='nodeUnHl( " + stat.id + ")' target='_blank' href='https://www.openstreetmap.org/" + ident.toLowerCase() + "/" + Math.abs(stat.osmid) + "'>" + Math.abs(stat.osmid) + "</a>";
203
 
233
 
204
         if (stat.attrs.name) {
234
         if (stat.attrs.name) {
205
-            row.innerHTML = "Node <a onmouseover='nodeHl( " + stat.id + ")' onmouseout='nodeUnHl( " + stat.id + ")' target='_blank' href='https://www.openstreetmap.org/node/" + stat.osmid + "'>" + stat.osmid + "</a> (<b>\"" + stat.attrs.name + "\"</b>)";
206
-        } else {
207
-            row.innerHTML = "Node <a onmouseover='nodeHl( " + stat.id + ")' onmouseout='nodeUnHl( " + stat.id + ")' target='_blank' href='https://www.openstreetmap.org/node/" + stat.osmid + "'>" + stat.osmid + "</a>";
235
+            row.innerHTML += " (<b>\"" + stat.attrs.name + "\"</b>)";
208
         }
236
         }
209
 
237
 
210
         if (grp.osmid == 1 || stat.orig_group != grp.id) newMembers.appendChild(row);
238
         if (grp.osmid == 1 || stat.orig_group != grp.id) newMembers.appendChild(row);
@@ -227,7 +255,8 @@ function renderGroup(grp, ll) {
227
         })
255
         })
228
         .setLatLng(ll)
256
         .setLatLng(ll)
229
         .setContent(content)
257
         .setContent(content)
230
-        .openOn(map);
258
+        .openOn(map)
259
+        .on('remove', function() {openGr = -1; groupUnHl(grp.id)});
231
 }
260
 }
232
 
261
 
233
 function openGroup(id, ll) {
262
 function openGroup(id, ll) {
@@ -247,7 +276,7 @@ function groupHl(id) {
247
     if (!document.groupIdx[id]) return;
276
     if (!document.groupIdx[id]) return;
248
     document.groupIdx[id].setStyle({
277
     document.groupIdx[id].setStyle({
249
         'weight': 6,
278
         'weight': 6,
250
-        'fillOpacity': 0.5
279
+        'color': "#eecc00"
251
     });
280
     });
252
 }
281
 }
253
 
282
 
@@ -255,7 +284,7 @@ function groupUnHl(id) {
255
     if (!document.groupIdx[id]) return;
284
     if (!document.groupIdx[id]) return;
256
     document.groupIdx[id].setStyle({
285
     document.groupIdx[id].setStyle({
257
         'weight': 3,
286
         'weight': 3,
258
-        'fillOpacity': 0.2
287
+        'color': document.groupIdx[id].options["fillColor"]
259
     });
288
     });
260
 }
289
 }
261
 
290
 
@@ -285,10 +314,10 @@ map.addControl(L.control.attribution({
285
     prefix: '&copy; <a href="http://ad.cs.uni-freiburg.de">University of Freiburg, Chair of Algorithms and Data Structures</a>'
314
     prefix: '&copy; <a href="http://ad.cs.uni-freiburg.de">University of Freiburg, Chair of Algorithms and Data Structures</a>'
286
 }));
315
 }));
287
 
316
 
288
-L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png', {
289
-    maxZoom: 23,
290
-    attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>, &copy; <a href="https://carto.com/attribution">CARTO</a>',
291
-    id: 'mapbox.streets'
317
+L.tileLayer('http://{s}.tile.stamen.com/toner-lite/{z}/{x}/{y}.png', {
318
+    maxZoom: 20,
319
+    attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
320
+    opacity: 0.8
292
 }).addTo(map);
321
 }).addTo(map);
293
 
322
 
294
 var layer = L.featureGroup().addTo(map);
323
 var layer = L.featureGroup().addTo(map);
@@ -368,7 +397,7 @@ function render() {
368
                     stat = content.stats[i];
397
                     stat = content.stats[i];
369
                     var ndMarker = marker(stat, map.getZoom());
398
                     var ndMarker = marker(stat, map.getZoom());
370
                     stations.push(ndMarker);
399
                     stations.push(ndMarker);
371
-                    document.nodeIdx[stat.id] = ndMarker;
400
+                    document.nodeIdx[stat.i] = ndMarker;
372
                 }
401
                 }
373
 
402
 
374
                 var groups = [];
403
                 var groups = [];
@@ -376,7 +405,7 @@ function render() {
376
                     group = content.groups[i];
405
                     group = content.groups[i];
377
                     var groupPoly = poly(group, map.getZoom());
406
                     var groupPoly = poly(group, map.getZoom());
378
                     groups.push(groupPoly);
407
                     groups.push(groupPoly);
379
-                    document.groupIdx[group.id] = groupPoly;
408
+                    document.groupIdx[group.i] = groupPoly;
380
                 }
409
                 }
381
 
410
 
382
                 var suggs = [];
411
                 var suggs = [];
@@ -402,10 +431,13 @@ function render() {
402
                 layer.addLayer(L.featureGroup(stations).on('click', function(a) {
431
                 layer.addLayer(L.featureGroup(stations).on('click', function(a) {
403
                     openStat(a.layer.options.id);
432
                     openStat(a.layer.options.id);
404
                 }));
433
                 }));
434
+
435
+                groupHl(openGr);
436
+                nodeHl(openSt);
405
             }
437
             }
406
         };
438
         };
407
 
439
 
408
-        window.xmlhttp.open("GET", backend + "/map?bbox=" + [map.getBounds().getSouthWest().lat, map.getBounds().getSouthWest().lng, map.getBounds().getNorthEast().lat, map.getBounds().getNorthEast().lng].join(","), true);
440
+        window.xmlhttp.open("GET", backend + "/map?z=" + map.getZoom() + "&bbox=" + [map.getBounds().getSouthWest().lat, map.getBounds().getSouthWest().lng, map.getBounds().getNorthEast().lat, map.getBounds().getNorthEast().lng].join(","), true);
409
         window.xmlhttp.send();
441
         window.xmlhttp.send();
410
     }
442
     }
411
 }
443
 }