Patrick Brosi 5 vuotta sitten
vanhempi
commit
667bcbd39e

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

@@ -8,11 +8,13 @@
8 8
 #include <sstream>
9 9
 #include <string>
10 10
 #include "osmfixer/index/StatIdx.h"
11
+#include "util/geo/BezierCurve.h"
11 12
 
12 13
 using osmfixer::StatIdx;
13 14
 using osmfixer::OsmAttrs;
14 15
 using osmfixer::Station;
15 16
 using osmfixer::Group;
17
+using osmfixer::Suggestion;
16 18
 
17 19
 // _____________________________________________________________________________
18 20
 void StatIdx::readFromFile(const std::string& path) {
@@ -90,6 +92,7 @@ void StatIdx::readFromFile(const std::string& path) {
90 92
   }
91 93
 
92 94
   initGroups();
95
+  initSuggestions();
93 96
   initIndex();
94 97
 }
95 98
 
@@ -126,7 +129,7 @@ void StatIdx::initGroups() {
126 129
     assert(_stations[i].group < _groups.size());
127 130
     if (_stations[i].group != _stations[i].origGroup &&
128 131
         _groups[_stations[i].group].osmid == 1) {
129
-			std::cout << "MURR" << std::endl;
132
+      std::cout << "MURR" << std::endl;
130 133
       // only add non-orig groups to polygons of new (non-osm) groups
131 134
       _groups[_stations[i].group].stations.push_back(i);
132 135
     }
@@ -157,8 +160,33 @@ void StatIdx::initIndex() {
157 160
                                                              false);
158 161
   _ggrid = util::geo::Grid<size_t, util::geo::Polygon, double>(5000, 5000,
159 162
                                                                _bbox, false);
163
+  _suggrid = util::geo::Grid<size_t, util::geo::Line, double>(5000, 5000, _bbox,
164
+                                                              false);
160 165
   for (size_t i = 0; i < _stations.size(); i++) _sgrid.add(_stations[i].pos, i);
161 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 345
 const Group* StatIdx::getGroup(size_t id) const {
213 346
   if (id >= _groups.size()) return 0;
214 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 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 32
 struct Station {
26 33
   size_t id;
27 34
   size_t osmid;
@@ -29,6 +36,7 @@ struct Station {
29 36
   util::geo::DPoint pos;
30 37
   OsmAttrs attrs;
31 38
   std::vector<AttrErr> attrErrs;
39
+  std::vector<size_t> suggestions;
32 40
 };
33 41
 
34 42
 struct Group {
@@ -47,9 +55,11 @@ class StatIdx {
47 55
 
48 56
   std::vector<const Station*> getStations(const util::geo::DBox bbox) const;
49 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 60
   const Station* getStation(size_t id) const;
52 61
   const Group* getGroup(size_t id) const;
62
+  const Suggestion* getSuggestion(size_t id) const;
53 63
 
54 64
  private:
55 65
   void addStation(size_t id, double lat, double lng, size_t origGroup,
@@ -58,13 +68,18 @@ class StatIdx {
58 68
 
59 69
   void initIndex();
60 70
   void initGroups();
71
+  void initSuggestions();
72
+
73
+  util::geo::DLine getGroupArrow(size_t stat, size_t group) const;
61 74
 
62 75
   std::vector<Station> _stations;
63 76
   std::vector<Group> _groups;
77
+  std::vector<Suggestion> _suggestions;
64 78
   util::geo::DBox _bbox;
65 79
 
66 80
   util::geo::Grid<size_t, util::geo::Point, double> _sgrid;
67 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 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 25
       a = util::http::Answer("200 OK", "osmfixer");
26 26
     } else if (cmd == "/map") {
27 27
       a = handleMapReq(params);
28
+    } else if (cmd == "/heatmap") {
29
+      a = handleHeatMapReq(params);
28 30
     } else if (cmd == "/stat") {
29 31
       a = handleStatReq(params);
30 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 127
 util::http::Answer StatServer::handleMapReq(const Params& pars) const {
50 128
   if (pars.count("bbox") == 0 || pars.find("bbox")->second.empty())
51 129
     throw std::invalid_argument("No bbox specified.");
@@ -92,6 +170,15 @@ util::http::Answer StatServer::handleMapReq(const Params& pars) const {
92 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 182
   json << "]}";
96 183
 
97 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 227
 void StatServer::printGroup(const Group* group, std::ostream* out) {
122 228
   std::vector<util::geo::DPoint> projPoly;
123 229
 
124 230
   for (auto p : group->poly.getOuter()) {
125 231
     projPoly.push_back(util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
126 232
   }
233
+
127 234
   (*out) << "{\"id\":" << group->id << ",\"poly\":[";
128 235
   char sep = ' ';
129 236
   for (auto p : projPoly) {
@@ -220,7 +327,8 @@ util::http::Answer StatServer::handleStatReq(const Params& pars) const {
220 327
   json << ",\"suggestions\":[";
221 328
 
222 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 332
     if (_idx->getGroup(stat->origGroup)->osmid < 2) {
225 333
       if (_idx->getGroup(stat->group)->osmid == 1) {
226 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 25
   static std::string parseUrl(std::string u, std::string pl, Params* params);
26 26
 
27 27
   util::http::Answer handleMapReq(const Params& pars) const;
28
+  util::http::Answer handleHeatMapReq(const Params& pars) const;
28 29
   util::http::Answer handleStatReq(const Params& pars) const;
29 30
 
30 31
   void printStation(const Station* stat, std::ostream* out) const;
31 32
   static void printGroup(const Group* stat, std::ostream* out);
33
+  static void printSugg(const Suggestion* stat, std::ostream* out);
32 34
 
33 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 27
 template <typename T>
28 28
 class BezierCurve {
29 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 33
   const PolyLine<T>& render(double d);
33 34