Patrick Brosi 5 years ago
parent
commit
667bcbd39e

+ 140 - 1
src/osmfixer/index/StatIdx.cpp

@@ -8,11 +8,13 @@
8
 #include <sstream>
8
 #include <sstream>
9
 #include <string>
9
 #include <string>
10
 #include "osmfixer/index/StatIdx.h"
10
 #include "osmfixer/index/StatIdx.h"
11
+#include "util/geo/BezierCurve.h"
11
 
12
 
12
 using osmfixer::StatIdx;
13
 using osmfixer::StatIdx;
13
 using osmfixer::OsmAttrs;
14
 using osmfixer::OsmAttrs;
14
 using osmfixer::Station;
15
 using osmfixer::Station;
15
 using osmfixer::Group;
16
 using osmfixer::Group;
17
+using osmfixer::Suggestion;
16
 
18
 
17
 // _____________________________________________________________________________
19
 // _____________________________________________________________________________
18
 void StatIdx::readFromFile(const std::string& path) {
20
 void StatIdx::readFromFile(const std::string& path) {
@@ -90,6 +92,7 @@ void StatIdx::readFromFile(const std::string& path) {
90
   }
92
   }
91
 
93
 
92
   initGroups();
94
   initGroups();
95
+  initSuggestions();
93
   initIndex();
96
   initIndex();
94
 }
97
 }
95
 
98
 
@@ -126,7 +129,7 @@ void StatIdx::initGroups() {
126
     assert(_stations[i].group < _groups.size());
129
     assert(_stations[i].group < _groups.size());
127
     if (_stations[i].group != _stations[i].origGroup &&
130
     if (_stations[i].group != _stations[i].origGroup &&
128
         _groups[_stations[i].group].osmid == 1) {
131
         _groups[_stations[i].group].osmid == 1) {
129
-			std::cout << "MURR" << std::endl;
132
+      std::cout << "MURR" << std::endl;
130
       // only add non-orig groups to polygons of new (non-osm) groups
133
       // only add non-orig groups to polygons of new (non-osm) groups
131
       _groups[_stations[i].group].stations.push_back(i);
134
       _groups[_stations[i].group].stations.push_back(i);
132
     }
135
     }
@@ -157,8 +160,33 @@ void StatIdx::initIndex() {
157
                                                              false);
160
                                                              false);
158
   _ggrid = util::geo::Grid<size_t, util::geo::Polygon, double>(5000, 5000,
161
   _ggrid = util::geo::Grid<size_t, util::geo::Polygon, double>(5000, 5000,
159
                                                                _bbox, false);
162
                                                                _bbox, false);
163
+  _suggrid = util::geo::Grid<size_t, util::geo::Line, double>(5000, 5000, _bbox,
164
+                                                              false);
160
   for (size_t i = 0; i < _stations.size(); i++) _sgrid.add(_stations[i].pos, i);
165
   for (size_t i = 0; i < _stations.size(); i++) _sgrid.add(_stations[i].pos, i);
161
   for (size_t i = 0; i < _groups.size(); i++) _ggrid.add(_groups[i].poly, i);
166
   for (size_t i = 0; i < _groups.size(); i++) _ggrid.add(_groups[i].poly, i);
167
+  for (size_t i = 0; i < _suggestions.size(); i++)
168
+    _suggrid.add(_suggestions[i].arrow, i);
169
+}
170
+
171
+// _____________________________________________________________________________
172
+std::vector<const Suggestion*> StatIdx::getSuggestions(
173
+    const util::geo::DBox bbox) const {
174
+  std::vector<const Suggestion*> ret;
175
+  auto ll = util::geo::latLngToWebMerc<double>(bbox.getLowerLeft().getX(),
176
+                                               bbox.getLowerLeft().getY());
177
+  auto ur = util::geo::latLngToWebMerc<double>(bbox.getUpperRight().getX(),
178
+                                               bbox.getUpperRight().getY());
179
+
180
+  std::set<size_t> tmp;
181
+  auto reqBox = util::geo::DBox(ll, ur);
182
+  _suggrid.get(reqBox, &tmp);
183
+
184
+  for (auto i : tmp) {
185
+    if (util::geo::intersects(_suggestions[i].arrow, reqBox))
186
+      ret.push_back(&_suggestions[i]);
187
+  }
188
+
189
+  return ret;
162
 }
190
 }
163
 
191
 
164
 // _____________________________________________________________________________
192
 // _____________________________________________________________________________
@@ -209,7 +237,118 @@ const Station* StatIdx::getStation(size_t id) const {
209
 }
237
 }
210
 
238
 
211
 // _____________________________________________________________________________
239
 // _____________________________________________________________________________
240
+void StatIdx::initSuggestions() {
241
+  for (size_t i = 0; i < _stations.size(); i++) {
242
+    auto& stat = _stations[i];
243
+    if (stat.group != stat.origGroup || getGroup(stat.group)->osmid == 1) {
244
+      Suggestion sug;
245
+      sug.station = i;
246
+      sug.arrow = util::geo::DLine{stat.pos, stat.pos};
247
+      if (getGroup(stat.origGroup)->osmid < 2) {
248
+        if (getGroup(stat.group)->osmid == 1) {
249
+          // move orphan into new group
250
+          sug.type = 1;
251
+          sug.target_gid = stat.group;
252
+
253
+          // sug.arrow = getGroupArrow(i, stat.group);
254
+
255
+          _suggestions.push_back(sug);
256
+          stat.suggestions.push_back(_suggestions.size() - 1);
257
+
258
+        } else if (getGroup(stat.group)->osmid > 1) {
259
+          // move orphan into existing group
260
+          sug.type = 2;
261
+          sug.target_gid = stat.group;
262
+          sug.target_osm_rel_id = getGroup(stat.group)->osmid;
263
+
264
+          sug.arrow = getGroupArrow(i, stat.group);
265
+
266
+          _suggestions.push_back(sug);
267
+          stat.suggestions.push_back(_suggestions.size() - 1);
268
+        }
269
+      } else {
270
+        if (getGroup(stat.group)->osmid == 1) {
271
+          // move station from relation into new group
272
+          sug.type = 3;
273
+          sug.orig_gid = stat.origGroup;
274
+          sug.orig_osm_rel_id = getGroup(stat.origGroup)->osmid;
275
+          sug.target_gid = stat.group;
276
+
277
+          sug.arrow = getGroupArrow(i, stat.group);
278
+
279
+          _suggestions.push_back(sug);
280
+          stat.suggestions.push_back(_suggestions.size() - 1);
281
+        } else if (getGroup(stat.group)->osmid > 1) {
282
+          // move station from relation into existing group
283
+          sug.type = 4;
284
+          sug.orig_gid = stat.origGroup;
285
+          sug.orig_osm_rel_id = getGroup(stat.origGroup)->osmid;
286
+          sug.target_gid = stat.group;
287
+          sug.target_osm_rel_id = getGroup(stat.group)->osmid;
288
+
289
+          _suggestions.push_back(sug);
290
+          stat.suggestions.push_back(_suggestions.size() - 1);
291
+        } else {
292
+          // move station out of relation
293
+          sug.type = 5;
294
+          sug.orig_gid = stat.origGroup;
295
+          sug.target_gid = stat.group;
296
+          sug.target_osm_rel_id = getGroup(stat.group)->osmid;
297
+
298
+          _suggestions.push_back(sug);
299
+          stat.suggestions.push_back(_suggestions.size() - 1);
300
+        }
301
+      }
302
+    }
303
+  }
304
+}
305
+
306
+// _____________________________________________________________________________
307
+util::geo::DLine StatIdx::getGroupArrow(size_t stat, size_t group) const {
308
+  auto a = getStation(stat)->pos;
309
+  auto b = util::geo::centroid(getGroup(group)->poly);
310
+
311
+  auto pl = util::geo::PolyLine<double>(a, b);
312
+  auto bb = pl.getPointAtDist(fmax(pl.getLength() / 2, pl.getLength() - 20)).p;
313
+
314
+  int side = rand() % 2;
315
+  if (!side) side = -1;
316
+
317
+
318
+  auto rot1 = util::geo::rotate(util::geo::DLine{a, bb}, 45 * side, a);
319
+  auto rot2 = util::geo::rotate(util::geo::DLine{a, bb}, -20 * side, bb);
320
+
321
+  auto rot3 = util::geo::rotate(util::geo::DLine{a, bb}, -120 * side, bb);
322
+
323
+  auto i =
324
+      util::geo::intersection(util::geo::LineSegment<double>{rot1[0], rot1[1]},
325
+                              util::geo::LineSegment<double>{rot2[0], rot2[1]});
326
+
327
+  auto line = util::geo::BezierCurve<double>(a, i, rot3[0], bb).render(1).getLine();
328
+
329
+  // arrowhead
330
+  auto pl2 = util::geo::PolyLine<double>(line);
331
+  util::geo::LineSegment<double> headSeg(pl2.getPointAtDist(pl2.getLength() - 5).p, line.back());
332
+  auto arrHeadLeft = util::geo::rotate(headSeg, -45, line.back()).first;
333
+  auto arrHeadRight = util::geo::rotate(headSeg, 45, line.back()).first;
334
+
335
+  auto tmp = line.back();
336
+
337
+  line.push_back(arrHeadLeft);
338
+  line.push_back(tmp);
339
+  line.push_back(arrHeadRight);
340
+
341
+  return line;
342
+}
343
+
344
+// _____________________________________________________________________________
212
 const Group* StatIdx::getGroup(size_t id) const {
345
 const Group* StatIdx::getGroup(size_t id) const {
213
   if (id >= _groups.size()) return 0;
346
   if (id >= _groups.size()) return 0;
214
   return &_groups[id];
347
   return &_groups[id];
215
 }
348
 }
349
+
350
+// _____________________________________________________________________________
351
+const Suggestion* StatIdx::getSuggestion(size_t id) const {
352
+  if (id >= _suggestions.size()) return 0;
353
+  return &_suggestions[id];
354
+}

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

@@ -22,6 +22,13 @@ struct AttrErr {
22
   size_t otherId;
22
   size_t otherId;
23
 };
23
 };
24
 
24
 
25
+struct Suggestion {
26
+  uint16_t type;
27
+  size_t station;
28
+  size_t target_gid, orig_gid, target_osm_rel_id, orig_osm_rel_id;
29
+  util::geo::DLine arrow;
30
+};
31
+
25
 struct Station {
32
 struct Station {
26
   size_t id;
33
   size_t id;
27
   size_t osmid;
34
   size_t osmid;
@@ -29,6 +36,7 @@ struct Station {
29
   util::geo::DPoint pos;
36
   util::geo::DPoint pos;
30
   OsmAttrs attrs;
37
   OsmAttrs attrs;
31
   std::vector<AttrErr> attrErrs;
38
   std::vector<AttrErr> attrErrs;
39
+  std::vector<size_t> suggestions;
32
 };
40
 };
33
 
41
 
34
 struct Group {
42
 struct Group {
@@ -47,9 +55,11 @@ class StatIdx {
47
 
55
 
48
   std::vector<const Station*> getStations(const util::geo::DBox bbox) const;
56
   std::vector<const Station*> getStations(const util::geo::DBox bbox) const;
49
   std::vector<const Group*> getGroups(const util::geo::DBox bbox) const;
57
   std::vector<const Group*> getGroups(const util::geo::DBox bbox) const;
58
+  std::vector<const Suggestion*> getSuggestions(const util::geo::DBox bbox) const;
50
 
59
 
51
   const Station* getStation(size_t id) const;
60
   const Station* getStation(size_t id) const;
52
   const Group* getGroup(size_t id) const;
61
   const Group* getGroup(size_t id) const;
62
+  const Suggestion* getSuggestion(size_t id) const;
53
 
63
 
54
  private:
64
  private:
55
   void addStation(size_t id, double lat, double lng, size_t origGroup,
65
   void addStation(size_t id, double lat, double lng, size_t origGroup,
@@ -58,13 +68,18 @@ class StatIdx {
58
 
68
 
59
   void initIndex();
69
   void initIndex();
60
   void initGroups();
70
   void initGroups();
71
+  void initSuggestions();
72
+
73
+  util::geo::DLine getGroupArrow(size_t stat, size_t group) const;
61
 
74
 
62
   std::vector<Station> _stations;
75
   std::vector<Station> _stations;
63
   std::vector<Group> _groups;
76
   std::vector<Group> _groups;
77
+  std::vector<Suggestion> _suggestions;
64
   util::geo::DBox _bbox;
78
   util::geo::DBox _bbox;
65
 
79
 
66
   util::geo::Grid<size_t, util::geo::Point, double> _sgrid;
80
   util::geo::Grid<size_t, util::geo::Point, double> _sgrid;
67
   util::geo::Grid<size_t, util::geo::Polygon, double> _ggrid;
81
   util::geo::Grid<size_t, util::geo::Polygon, double> _ggrid;
82
+  util::geo::Grid<size_t, util::geo::Line, double> _suggrid;
68
 };
83
 };
69
 }  // namespace osmfixer
84
 }  // namespace osmfixer
70
 
85
 

+ 109 - 1
src/osmfixer/server/StatServer.cpp

@@ -25,6 +25,8 @@ util::http::Answer StatServer::handle(const util::http::Req& req,
25
       a = util::http::Answer("200 OK", "osmfixer");
25
       a = util::http::Answer("200 OK", "osmfixer");
26
     } else if (cmd == "/map") {
26
     } else if (cmd == "/map") {
27
       a = handleMapReq(params);
27
       a = handleMapReq(params);
28
+    } else if (cmd == "/heatmap") {
29
+      a = handleHeatMapReq(params);
28
     } else if (cmd == "/stat") {
30
     } else if (cmd == "/stat") {
29
       a = handleStatReq(params);
31
       a = handleStatReq(params);
30
     } else {
32
     } else {
@@ -46,6 +48,82 @@ util::http::Answer StatServer::handle(const util::http::Req& req,
46
 }
48
 }
47
 
49
 
48
 // _____________________________________________________________________________
50
 // _____________________________________________________________________________
51
+util::http::Answer StatServer::handleHeatMapReq(const Params& pars) const {
52
+  if (pars.count("bbox") == 0 || pars.find("bbox")->second.empty())
53
+    throw std::invalid_argument("No bbox specified.");
54
+  std::string cb;
55
+  if (pars.count("cb")) cb = pars.find("cb")->second.c_str();
56
+  auto box = util::split(pars.find("bbox")->second, ',');
57
+
58
+  if (box.size() != 4) throw std::invalid_argument("Invalid request.");
59
+
60
+  double lat1 = atof(box[0].c_str());
61
+  double lng1 = atof(box[1].c_str());
62
+  double lat2 = atof(box[2].c_str());
63
+  double lng2 = atof(box[3].c_str());
64
+
65
+  std::cout << pars.find("bbox")->second << std::endl;
66
+
67
+  util::geo::DBox bbox(util::geo::DPoint(lat1, lng1),
68
+                       util::geo::DPoint(lat2, lng2));
69
+
70
+  std::stringstream json;
71
+
72
+  json << std::setprecision(10);
73
+
74
+  if (cb.size()) json << cb << "(";
75
+  json << "{\"ok\":[";
76
+
77
+  auto ret = _idx->getStations(bbox);
78
+  char sep = ' ';
79
+  for (auto stat : ret) {
80
+    if (stat->suggestions.size() != 0 || stat->attrErrs.size() != 0) continue;
81
+    json << sep;
82
+    sep = ',';
83
+
84
+    auto latLng =
85
+        util::geo::webMercToLatLng<double>(stat->pos.getX(), stat->pos.getY());
86
+    json << "[" << latLng.getY() << "," << latLng.getX() << "," << "0.5" << "]";
87
+  }
88
+
89
+
90
+  json << "],\"sugg\":[";
91
+
92
+  sep = ' ';
93
+  for (auto stat : ret) {
94
+    if (stat->suggestions.size() == 0) continue;
95
+    json << sep;
96
+    sep = ',';
97
+
98
+    auto latLng =
99
+        util::geo::webMercToLatLng<double>(stat->pos.getX(), stat->pos.getY());
100
+    json << "[" << latLng.getY() << "," << latLng.getX() << "," << "0.5" << "]";
101
+  }
102
+
103
+  json << "],\"err\":[";
104
+
105
+  sep = ' ';
106
+  for (auto stat : ret) {
107
+    if (stat->attrErrs.size() == 0) continue;
108
+    json << sep;
109
+    sep = ',';
110
+
111
+    auto latLng =
112
+        util::geo::webMercToLatLng<double>(stat->pos.getX(), stat->pos.getY());
113
+    json << "[" << latLng.getY() << "," << latLng.getX() << "," << "0.5" << "]";
114
+  }
115
+
116
+  json << "]}";
117
+
118
+  if (cb.size()) json << ")";
119
+
120
+  auto answ = util::http::Answer("200 OK", json.str(), true);
121
+  answ.params["Content-Type"] = "application/javascript; charset=utf-8";
122
+
123
+  return answ;
124
+}
125
+
126
+// _____________________________________________________________________________
49
 util::http::Answer StatServer::handleMapReq(const Params& pars) const {
127
 util::http::Answer StatServer::handleMapReq(const Params& pars) const {
50
   if (pars.count("bbox") == 0 || pars.find("bbox")->second.empty())
128
   if (pars.count("bbox") == 0 || pars.find("bbox")->second.empty())
51
     throw std::invalid_argument("No bbox specified.");
129
     throw std::invalid_argument("No bbox specified.");
@@ -92,6 +170,15 @@ util::http::Answer StatServer::handleMapReq(const Params& pars) const {
92
     printGroup(group, &json);
170
     printGroup(group, &json);
93
   }
171
   }
94
 
172
 
173
+  json << "], \"suggestions\":[";
174
+  auto suggs = _idx->getSuggestions(bbox);
175
+  sep = ' ';
176
+  for (auto sugg : suggs) {
177
+    json << sep;
178
+    sep = ',';
179
+    printSugg(sugg, &json);
180
+  }
181
+
95
   json << "]}";
182
   json << "]}";
96
 
183
 
97
   if (cb.size()) json << ")";
184
   if (cb.size()) json << ")";
@@ -118,12 +205,32 @@ void StatServer::printStation(const Station* stat, std::ostream* out) const {
118
 }
205
 }
119
 
206
 
120
 // _____________________________________________________________________________
207
 // _____________________________________________________________________________
208
+void StatServer::printSugg(const Suggestion* sugg, std::ostream* out) {
209
+  std::vector<util::geo::DPoint> projArrow;
210
+
211
+  for (auto p : sugg->arrow) {
212
+    projArrow.push_back(util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
213
+  }
214
+
215
+  (*out) << "{\"id\":" << sugg->station << ",\"type\":" << sugg->type
216
+         << ",\"arrow\":[";
217
+  char sep = ' ';
218
+  for (auto p : projArrow) {
219
+    (*out) << sep << "[" << p.getY() << "," << p.getX() << "]";
220
+    sep = ',';
221
+  }
222
+  (*out) << "]";
223
+  (*out) << "}";
224
+}
225
+
226
+// _____________________________________________________________________________
121
 void StatServer::printGroup(const Group* group, std::ostream* out) {
227
 void StatServer::printGroup(const Group* group, std::ostream* out) {
122
   std::vector<util::geo::DPoint> projPoly;
228
   std::vector<util::geo::DPoint> projPoly;
123
 
229
 
124
   for (auto p : group->poly.getOuter()) {
230
   for (auto p : group->poly.getOuter()) {
125
     projPoly.push_back(util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
231
     projPoly.push_back(util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
126
   }
232
   }
233
+
127
   (*out) << "{\"id\":" << group->id << ",\"poly\":[";
234
   (*out) << "{\"id\":" << group->id << ",\"poly\":[";
128
   char sep = ' ';
235
   char sep = ' ';
129
   for (auto p : projPoly) {
236
   for (auto p : projPoly) {
@@ -220,7 +327,8 @@ util::http::Answer StatServer::handleStatReq(const Params& pars) const {
220
   json << ",\"suggestions\":[";
327
   json << ",\"suggestions\":[";
221
 
328
 
222
   // suggestions
329
   // suggestions
223
-  if (stat->group != stat->origGroup || _idx->getGroup(stat->group)->osmid == 1) {
330
+  if (stat->group != stat->origGroup ||
331
+      _idx->getGroup(stat->group)->osmid == 1) {
224
     if (_idx->getGroup(stat->origGroup)->osmid < 2) {
332
     if (_idx->getGroup(stat->origGroup)->osmid < 2) {
225
       if (_idx->getGroup(stat->group)->osmid == 1) {
333
       if (_idx->getGroup(stat->group)->osmid == 1) {
226
         json << "{\"type\": 1, \"target_gid\":" << stat->group << "}";
334
         json << "{\"type\": 1, \"target_gid\":" << stat->group << "}";

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

@@ -25,10 +25,12 @@ class StatServer : public util::http::Handler {
25
   static std::string parseUrl(std::string u, std::string pl, Params* params);
25
   static std::string parseUrl(std::string u, std::string pl, Params* params);
26
 
26
 
27
   util::http::Answer handleMapReq(const Params& pars) const;
27
   util::http::Answer handleMapReq(const Params& pars) const;
28
+  util::http::Answer handleHeatMapReq(const Params& pars) const;
28
   util::http::Answer handleStatReq(const Params& pars) const;
29
   util::http::Answer handleStatReq(const Params& pars) const;
29
 
30
 
30
   void printStation(const Station* stat, std::ostream* out) const;
31
   void printStation(const Station* stat, std::ostream* out) const;
31
   static void printGroup(const Group* stat, std::ostream* out);
32
   static void printGroup(const Group* stat, std::ostream* out);
33
+  static void printSugg(const Suggestion* stat, std::ostream* out);
32
 
34
 
33
   const osmfixer::StatIdx* _idx;
35
   const osmfixer::StatIdx* _idx;
34
 };
36
 };

BIN
src/util/geo/.PolyLine.h.swp


+ 2 - 1
src/util/geo/BezierCurve.h

@@ -27,7 +27,8 @@ struct CubicPolynom {
27
 template <typename T>
27
 template <typename T>
28
 class BezierCurve {
28
 class BezierCurve {
29
  public:
29
  public:
30
-  BezierCurve(const Point<T>& a, const Point<T>& b, const Point<T>& c, const Point<T>& d);
30
+  BezierCurve(const Point<T>& a, const Point<T>& b, const Point<T>& c,
31
+              const Point<T>& d);
31
 
32
 
32
   const PolyLine<T>& render(double d);
33
   const PolyLine<T>& render(double d);
33
 
34