Patrick Brosi 4 years ago
parent
commit
5d548486cb

+ 1 - 1
src/osmfixer/CMakeLists.txt

@@ -20,7 +20,7 @@ add_custom_command(
20
 
20
 
21
 add_custom_command(
21
 add_custom_command(
22
 	OUTPUT build.h
22
 	OUTPUT build.h
23
-	COMMAND cd ${PROJECT_BINARY_DIR}/../web/ && cat leaflet-heat.js script.js > build.js && xxd -i build.js > ${CMAKE_CURRENT_BINARY_DIR}/build.h && rm build.js
23
+	COMMAND cd ${PROJECT_BINARY_DIR}/../web/ && cat leaflet.js leaflet-heat.js script.js > merged.js && java -jar closurec/compiler.jar -O SIMPLE leaflet.js leaflet-heat.js script.js > build.js && xxd -i build.js > ${CMAKE_CURRENT_BINARY_DIR}/build.h && rm build.js
24
 	DEPENDS "${PROJECT_BINARY_DIR}/../web/script.js"
24
 	DEPENDS "${PROJECT_BINARY_DIR}/../web/script.js"
25
 	DEPENDS "${PROJECT_BINARY_DIR}/../web/leaflet-heat.js"
25
 	DEPENDS "${PROJECT_BINARY_DIR}/../web/leaflet-heat.js"
26
 	VERBATIM
26
 	VERBATIM

+ 2 - 1
src/osmfixer/FixerMain.cpp

@@ -56,6 +56,7 @@ int main(int argc, char** argv) {
56
   std::vector<std::pair<IdRange, StatIdx>> idx(inputPaths.size());
56
   std::vector<std::pair<IdRange, StatIdx>> idx(inputPaths.size());
57
   IdRange idRange{0, 0, 0, 0, 0, 0};
57
   IdRange idRange{0, 0, 0, 0, 0, 0};
58
   for (size_t i = 0; i < inputPaths.size(); i++) {
58
   for (size_t i = 0; i < inputPaths.size(); i++) {
59
+    LOG(INFO) << "Processing " << inputPaths[i];
59
     idx[i].first = idRange;
60
     idx[i].first = idRange;
60
     if (i > 0) {
61
     if (i > 0) {
61
       idx[i].first.sidStart = idx[i - 1].first.sidEnd + 1;
62
       idx[i].first.sidStart = idx[i - 1].first.sidEnd + 1;
@@ -70,7 +71,7 @@ int main(int argc, char** argv) {
70
   }
71
   }
71
 
72
 
72
   LOG(INFO) << "Building search index...";
73
   LOG(INFO) << "Building search index...";
73
-  osmfixer::SearchIdx searchIdx(idx.front().second);
74
+  osmfixer::SearchIdx searchIdx(idx);
74
 
75
 
75
   LOG(INFO) << "Starting server...";
76
   LOG(INFO) << "Starting server...";
76
   StatServer serv(idx, searchIdx);
77
   StatServer serv(idx, searchIdx);

+ 67 - 58
src/osmfixer/index/SearchIdx.cpp

@@ -14,57 +14,64 @@
14
 #include "osmfixer/index/SearchIdx.h"
14
 #include "osmfixer/index/SearchIdx.h"
15
 
15
 
16
 using osmfixer::SearchIdx;
16
 using osmfixer::SearchIdx;
17
-using osmfixer::TupleList;
18
 using osmfixer::TripleList;
17
 using osmfixer::TripleList;
18
+using osmfixer::TupleList;
19
 
19
 
20
 // _____________________________________________________________________________
20
 // _____________________________________________________________________________
21
-void SearchIdx::build() {
21
+void SearchIdx::build(std::vector<std::pair<IdRange, StatIdx>>& idxs) {
22
   _qGramIndex.clear();
22
   _qGramIndex.clear();
23
 
23
 
24
   size_t nameid = 0;
24
   size_t nameid = 0;
25
 
25
 
26
   std::map<std::wstring, size_t> tokenIds;
26
   std::map<std::wstring, size_t> tokenIds;
27
 
27
 
28
-  for (size_t gid = 0; gid < _stats.getGroups().size(); gid++) {
29
-		auto group = _stats.getGroup(gid);
30
-
31
-		// dont index empty groups
32
-		if (group->stations.size() == 0) continue;
33
-		if (group->polyStations.size() == 0) continue;
34
-
35
-    for (const auto& name : _stats.getGroup(gid)->uniqueNames) {
36
-      // use wstring to get UTF-8 chars right
37
-      std::wstring wname =
38
-          std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(name);
39
-
40
-      _nameToGroup[nameid] = gid;
41
-			_names.push_back(name);
42
-
43
-      for (const auto& token : tokenize(wname)) {
44
-        if (tokenIds.count(token)) {
45
-          if (_inv[tokenIds.find(token)->second].size() == 0 ||
46
-              _inv[tokenIds.find(token)->second].back().first != nameid) {
47
-            // only use a token once per station
48
-            _inv[tokenIds.find(token)->second].push_back({nameid, 1});
28
+  for (const auto& idx : idxs) {
29
+    for (size_t gid = 0; gid < idx.second.getGroups().size(); gid++) {
30
+      auto group = idx.second.getGroup(gid);
31
+
32
+      // slightly prefer larger stations
33
+      double groupScore = 1.0 + log(group->stations.size()) / 20;
34
+
35
+      // dont index empty groups
36
+      if (group->stations.size() == 0) continue;
37
+      if (group->polyStations.size() == 0) continue;
38
+
39
+      for (const auto& namep : group->uniqueNames) {
40
+        // use wstring to get UTF-8 chars right
41
+        const auto& name = namep.second;
42
+        std::wstring wname =
43
+            std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(name);
44
+
45
+        _nameToGroup[nameid] = idx.first.gidStart + gid;
46
+        _names.push_back(name);
47
+
48
+        for (const auto& token : tokenize(wname)) {
49
+          if (tokenIds.count(token)) {
50
+            if (_inv[tokenIds.find(token)->second].size() == 0 ||
51
+                _inv[tokenIds.find(token)->second].back().first != nameid) {
52
+              // only use a token once per station
53
+              _inv[tokenIds.find(token)->second].push_back(
54
+                  {nameid, groupScore});
55
+            }
56
+          } else {
57
+            tokenIds[token] = _tokens.size();
58
+            _tokens.push_back(token);
59
+            _inv.push_back(TupleList());
60
+            _inv.back().push_back({nameid, groupScore});
49
           }
61
           }
50
-        } else {
51
-          tokenIds[token] = _tokens.size();
52
-          _tokens.push_back(token);
53
-          _inv.push_back(TupleList());
54
-          _inv.back().push_back({nameid, 1});
55
-        }
56
 
62
 
57
-        size_t tokenId = tokenIds.find(token)->second;
58
-        for (const auto& qGram : getQGrams(token)) {
59
-          if (_qGramIndex[qGram].size() &&
60
-              _qGramIndex[qGram].back().first == tokenId) {
61
-            _qGramIndex[qGram].back().second++;
62
-          } else {
63
-            _qGramIndex[qGram].push_back({tokenId, 1});
63
+          size_t tokenId = tokenIds.find(token)->second;
64
+          for (const auto& qGram : getQGrams(token)) {
65
+            if (_qGramIndex[qGram].size() &&
66
+                _qGramIndex[qGram].back().first == tokenId) {
67
+              _qGramIndex[qGram].back().second++;
68
+            } else {
69
+              _qGramIndex[qGram].push_back({tokenId, 1});
70
+            }
64
           }
71
           }
65
         }
72
         }
73
+        nameid++;
66
       }
74
       }
67
-      nameid++;
68
     }
75
     }
69
   }
76
   }
70
 
77
 
@@ -101,7 +108,7 @@ std::vector<std::wstring> SearchIdx::tokenize(const std::wstring& str) {
101
   std::vector<std::wstring> ret;
108
   std::vector<std::wstring> ret;
102
   std::wstring cur;
109
   std::wstring cur;
103
 
110
 
104
-  const wchar_t* seps = L"_-?'\"|!@#$%^&*()_+}|><.,\\";
111
+  const wchar_t* seps = L"_-?'\"|!@#$%^&*()_+{}[]|<>.:,\\/";
105
 
112
 
106
   for (size_t i = 0; i < str.size(); i++) {
113
   for (size_t i = 0; i < str.size(); i++) {
107
     if (std::iswspace(str[i]) || wcschr(seps, str[i])) {
114
     if (std::iswspace(str[i]) || wcschr(seps, str[i])) {
@@ -147,42 +154,43 @@ TripleList SearchIdx::find(const std::string& qry) const {
147
     double delta = token.size() / 4.0;
154
     double delta = token.size() / 4.0;
148
     auto res = tokenFind(token);
155
     auto res = tokenFind(token);
149
 
156
 
150
-    std::partial_sort(res.begin(), res.begin() + std::min<size_t>(100, res.size()), res.end(), resComp);
157
+    std::partial_sort(res.begin(),
158
+                      res.begin() + std::min<size_t>(100, res.size()),
159
+                      res.end(), resComp);
151
 
160
 
152
     std::map<size_t, double> bests;
161
     std::map<size_t, double> bests;
153
     std::map<size_t, size_t> bestToken;
162
     std::map<size_t, size_t> bestToken;
154
 
163
 
155
-		size_t TOPK = 100;
164
+    size_t TOPK = 100;
156
 
165
 
157
-		// res contains the 100 best token matches
166
+    // res contains the 100 best token matches
158
     for (size_t i = 0; i < res.size() && i < TOPK; i++) {
167
     for (size_t i = 0; i < res.size() && i < TOPK; i++) {
159
       for (size_t j = 0; j < _inv[res[i].first].size(); j++) {
168
       for (size_t j = 0; j < _inv[res[i].first].size(); j++) {
169
+        double score =
170
+            _inv[res[i].first][j].second * (delta / (1.0 + res[i].second));
160
 
171
 
161
-				double score = _inv[res[i].first][j].second * (delta / (1.0 + res[i].second));
162
-
163
-				if (score > bests[_inv[res[i].first][j].first]) {
164
-						bests[_inv[res[i].first][j].first] = score;
165
-						bestToken[_inv[res[i].first][j].first] = res[i].first;
166
-				}
172
+        if (score > bests[_inv[res[i].first][j].first]) {
173
+          bests[_inv[res[i].first][j].first] = score;
174
+          bestToken[_inv[res[i].first][j].first] = res[i].first;
175
+        }
167
       }
176
       }
168
     }
177
     }
169
 
178
 
170
     for (size_t i = 0; i < res.size() && i < TOPK; i++) {
179
     for (size_t i = 0; i < res.size() && i < TOPK; i++) {
171
-			// inv[res[i]] contains all the names the token res[i] occurs in
180
+      // inv[res[i]] contains all the names the token res[i] occurs in
172
 
181
 
173
       lists.push_back(_inv[res[i].first]);
182
       lists.push_back(_inv[res[i].first]);
174
 
183
 
175
       // give them a score based on their PED
184
       // give them a score based on their PED
176
-			for (size_t j = 0; j < lists.back().size(); j++) {
177
-				double score = lists.back()[j].second *
178
-                                 (delta / (1.0 + res[i].second));
179
-				// best is the token for this name that matched best for the input token
180
-				size_t best = bestToken[lists.back()[j].first];
181
-
182
-				// if it is not this token, we dont count it
183
-				if (res[i].first != best) score = 0;
185
+      for (size_t j = 0; j < lists.back().size(); j++) {
186
+        double score = lists.back()[j].second * (delta / (1.0 + res[i].second));
187
+        // best is the token for this name that matched best for the input token
188
+        size_t best = bestToken[lists.back()[j].first];
189
+
190
+        // if it is not this token, we dont count it
191
+        if (res[i].first != best) score = 0;
184
         lists.back()[j].second = score;
192
         lists.back()[j].second = score;
185
-			}
193
+      }
186
     }
194
     }
187
   }
195
   }
188
 
196
 
@@ -201,7 +209,8 @@ TripleList SearchIdx::find(const std::string& qry) const {
201
 
209
 
202
   TripleList fin;
210
   TripleList fin;
203
   for (const auto& r : fr) {
211
   for (const auto& r : fr) {
204
-    if (fin.size() == 0 || fin.back().first.first != r.first.first) fin.push_back(r);
212
+    if (fin.size() == 0 || fin.back().first.first != r.first.first)
213
+      fin.push_back(r);
205
   }
214
   }
206
 
215
 
207
   std::partial_sort(fin.begin(), fin.begin() + std::min<size_t>(fin.size(), 10),
216
   std::partial_sort(fin.begin(), fin.begin() + std::min<size_t>(fin.size(), 10),

+ 8 - 5
src/osmfixer/index/SearchIdx.h

@@ -29,15 +29,20 @@ inline bool resCompInv(const Triple& lh, const Triple& rh) {
29
 
29
 
30
 inline bool resGidCompInv(const Triple& lh, const Triple& rh) {
30
 inline bool resGidCompInv(const Triple& lh, const Triple& rh) {
31
   if (lh.first.first < rh.first.first) return true;
31
   if (lh.first.first < rh.first.first) return true;
32
-  if (lh.first.first == rh.first.first) return lh.second > rh.second;
32
+  if (lh.first.first == rh.first.first) {
33
+    // if the scores are equal, order by the name id to prefer the original
34
+    // name id, which is lowest!
35
+    if (fabs(lh.second - rh.second) < 0.01) return lh.first.second < rh.first.second;
36
+    return lh.second > rh.second;
37
+  }
33
   return false;
38
   return false;
34
 }
39
 }
35
 
40
 
36
 class SearchIdx {
41
 class SearchIdx {
37
  public:
42
  public:
38
-  explicit SearchIdx(const StatIdx& sidx) : _stats(sidx) { build(); }
43
+  explicit SearchIdx(std::vector<std::pair<IdRange, StatIdx>>& idxs) { build(idxs); }
39
 
44
 
40
-  void build();
45
+  void build(std::vector<std::pair<IdRange, StatIdx>>& idxs);
41
 
46
 
42
   TripleList find(const std::string& qry) const;
47
   TripleList find(const std::string& qry) const;
43
 
48
 
@@ -54,8 +59,6 @@ class SearchIdx {
54
   TupleList tokenFind(const std::wstring& prefix) const;
59
   TupleList tokenFind(const std::wstring& prefix) const;
55
   static TupleList lmerge(const std::vector<const TupleList*>& lists);
60
   static TupleList lmerge(const std::vector<const TupleList*>& lists);
56
 
61
 
57
-  const StatIdx& _stats;
58
-
59
   Index _qGramIndex;
62
   Index _qGramIndex;
60
 
63
 
61
   std::unordered_map<size_t, size_t> _nameToGroup;
64
   std::unordered_map<size_t, size_t> _nameToGroup;

+ 29 - 11
src/osmfixer/index/StatIdx.cpp

@@ -210,7 +210,6 @@ void StatIdx::addGroup(size_t osmid, const OsmAttrs& attrs) {
210
 // _____________________________________________________________________________
210
 // _____________________________________________________________________________
211
 void StatIdx::initGroups() {
211
 void StatIdx::initGroups() {
212
   for (size_t i = 0; i < _stations.size(); i++) {
212
   for (size_t i = 0; i < _stations.size(); i++) {
213
-
214
     // this should be ensured by the input file
213
     // this should be ensured by the input file
215
     assert(_stations[i].origGroup < _groups.size());
214
     assert(_stations[i].origGroup < _groups.size());
216
     _groups[_stations[i].origGroup].polyStations.push_back(i);
215
     _groups[_stations[i].origGroup].polyStations.push_back(i);
@@ -238,24 +237,26 @@ void StatIdx::initGroups() {
238
         if (usedGroupNames.count(name)) continue;
237
         if (usedGroupNames.count(name)) continue;
239
         usedGroupNames.insert(name);
238
         usedGroupNames.insert(name);
240
 
239
 
241
-        _groups[gid].uniqueNames.push_back(name);
240
+        // relation names are preferred
241
+        _groups[gid].uniqueNames.push_back({nameAttrRel(np.first) + 3, name});
242
       }
242
       }
243
     }
243
     }
244
 
244
 
245
-    for (size_t sid :_groups[gid].stations) {
245
+    for (size_t sid : _groups[gid].stations) {
246
       for (const auto& np : _stations[sid].attrs) {
246
       for (const auto& np : _stations[sid].attrs) {
247
         for (const auto& name : np.second) {
247
         for (const auto& name : np.second) {
248
           // use every name only once to keep the index small
248
           // use every name only once to keep the index small
249
           if (usedGroupNames.count(name)) continue;
249
           if (usedGroupNames.count(name)) continue;
250
           usedGroupNames.insert(name);
250
           usedGroupNames.insert(name);
251
 
251
 
252
-          _groups[gid].uniqueNames.push_back(name);
252
+          _groups[gid].uniqueNames.push_back({nameAttrRel(np.first), name});
253
         }
253
         }
254
       }
254
       }
255
     }
255
     }
256
 
256
 
257
     // take the longest name as the identifier
257
     // take the longest name as the identifier
258
-    std::sort(_groups[gid].uniqueNames.begin(), _groups[gid].uniqueNames.end(), byLen);
258
+    std::sort(_groups[gid].uniqueNames.begin(), _groups[gid].uniqueNames.end(),
259
+              byAttrScore);
259
   }
260
   }
260
 
261
 
261
   // build hull polygon
262
   // build hull polygon
@@ -284,7 +285,6 @@ util::geo::Polygon<double> StatIdx::hull(
284
   util::geo::MultiPoint<double> mp;
285
   util::geo::MultiPoint<double> mp;
285
   util::geo::Polygon<double> ret;
286
   util::geo::Polygon<double> ret;
286
   for (const auto& geom : imp) {
287
   for (const auto& geom : imp) {
287
-    // double rad = 11.0;
288
     int n = 20;
288
     int n = 20;
289
     for (int i = 0; i < n; i++) {
289
     for (int i = 0; i < n; i++) {
290
       double x = rad * cos((2.0 * M_PI / static_cast<double>(n)) *
290
       double x = rad * cos((2.0 * M_PI / static_cast<double>(n)) *
@@ -311,7 +311,10 @@ void StatIdx::initIndex() {
311
                                                               _bbox, false);
311
                                                               _bbox, false);
312
 
312
 
313
   for (size_t i = 0; i < _stations.size(); i++) _sgrid.add(_stations[i].pos, i);
313
   for (size_t i = 0; i < _stations.size(); i++) _sgrid.add(_stations[i].pos, i);
314
-  for (size_t i = 0; i < _groups.size(); i++) _ggrid.add(_groups[i].poly, i);
314
+  for (size_t i = 0; i < _groups.size(); i++) {
315
+    if (_groups[i].polyStations.size() == 1 && _groups[i].osmid < 2) continue;
316
+    _ggrid.add(_groups[i].poly, i);
317
+  }
315
   for (size_t i = 0; i < _suggestions.size(); i++)
318
   for (size_t i = 0; i < _suggestions.size(); i++)
316
     _suggrid.add(_suggestions[i].arrow, i);
319
     _suggrid.add(_suggestions[i].arrow, i);
317
 
320
 
@@ -566,7 +569,7 @@ void StatIdx::initSuggestions() {
566
   for (size_t i = 0; i < _groups.size(); i++) {
569
   for (size_t i = 0; i < _groups.size(); i++) {
567
     auto& group = _groups[i];
570
     auto& group = _groups[i];
568
 
571
 
569
-    if (group.attrs.size() == 0) {
572
+    if (group.attrs.count("name") == 0) {
570
       Suggestion sug;
573
       Suggestion sug;
571
       sug.station = i;
574
       sug.station = i;
572
       sug.type = 7;
575
       sug.type = 7;
@@ -616,7 +619,7 @@ void StatIdx::initSuggestions() {
616
   for (size_t i = 0; i < _stations.size(); i++) {
619
   for (size_t i = 0; i < _stations.size(); i++) {
617
     auto& stat = _stations[i];
620
     auto& stat = _stations[i];
618
 
621
 
619
-    if (stat.attrs.size() == 0) {
622
+    if (stat.attrs.count("name") == 0 && _groups[stat.group].attrs.count("name") == 0) {
620
       Suggestion sug;
623
       Suggestion sug;
621
       sug.station = i;
624
       sug.station = i;
622
       sug.type = 7;
625
       sug.type = 7;
@@ -671,7 +674,8 @@ void StatIdx::initSuggestions() {
671
       sug.arrow = util::geo::DLine{centroid, centroid};
674
       sug.arrow = util::geo::DLine{centroid, centroid};
672
 
675
 
673
       if (getGroup(stat.origGroup)->osmid < 2) {
676
       if (getGroup(stat.origGroup)->osmid < 2) {
674
-        if (getGroup(stat.group)->osmid == 1) {
677
+        if (getGroup(stat.group)->osmid == 1 &&
678
+            getGroup(stat.group)->stations.size() > 1) {
675
           // move orphan into new group
679
           // move orphan into new group
676
           sug.type = 1;
680
           sug.type = 1;
677
           sug.target_gid = stat.group;
681
           sug.target_gid = stat.group;
@@ -739,7 +743,6 @@ void StatIdx::initSuggestions() {
739
         }
743
         }
740
       }
744
       }
741
     }
745
     }
742
-
743
   }
746
   }
744
 }
747
 }
745
 
748
 
@@ -799,6 +802,21 @@ const Group* StatIdx::getGroup(size_t id) const {
799
 }
802
 }
800
 
803
 
801
 // _____________________________________________________________________________
804
 // _____________________________________________________________________________
805
+int StatIdx::nameAttrRel(const std::string& attr) const {
806
+  if (attr == "uic_name") return 5;
807
+  if (attr == "ref_name") return 5;
808
+  if (attr == "nat_name") return 4;
809
+  if (attr == "official_name") return 3;
810
+  if (attr == "gtfs_name") return 3;
811
+  if (attr == "int_name") return 2;
812
+  if (attr == "name") return 1;
813
+
814
+  if (attr == "alt_name") return -1;
815
+
816
+  return 0;
817
+}
818
+
819
+// _____________________________________________________________________________
802
 const Suggestion* StatIdx::getSuggestion(size_t id) const {
820
 const Suggestion* StatIdx::getSuggestion(size_t id) const {
803
   if (id >= _suggestions.size()) return 0;
821
   if (id >= _suggestions.size()) return 0;
804
   return &_suggestions[id];
822
   return &_suggestions[id];

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

@@ -15,10 +15,15 @@ namespace osmfixer {
15
 
15
 
16
 typedef std::map<std::string, std::vector<std::string>> OsmAttrs;
16
 typedef std::map<std::string, std::vector<std::string>> OsmAttrs;
17
 
17
 
18
-inline bool byLen(const std::string& lh, const std::string& rh) {
19
-  return lh.size() > rh.size();
18
+inline bool byAttrScore(const std::pair<int, std::string>& lh,
19
+                  const std::pair<int, std::string>& rh) {
20
+  return lh.first > rh.first;
20
 }
21
 }
21
 
22
 
23
+struct IdRange {
24
+  size_t sidStart, sidEnd, gidStart, gidEnd, suggIdStart, suggIdEnd;
25
+};
26
+
22
 struct AttrErr {
27
 struct AttrErr {
23
   std::string attr;
28
   std::string attr;
24
   std::string otherAttr;
29
   std::string otherAttr;
@@ -60,7 +65,7 @@ struct Group {
60
   std::vector<size_t> polyStations;
65
   std::vector<size_t> polyStations;
61
   std::vector<AttrErr> attrErrs;
66
   std::vector<AttrErr> attrErrs;
62
   std::vector<size_t> suggestions;
67
   std::vector<size_t> suggestions;
63
-  std::vector<std::string> uniqueNames;
68
+  std::vector<std::pair<int, std::string>> uniqueNames;
64
 };
69
 };
65
 
70
 
66
 struct Cluster {
71
 struct Cluster {
@@ -101,7 +106,6 @@ class StatIdx {
101
   const std::vector<Station>& getStations() const { return _stations; }
106
   const std::vector<Station>& getStations() const { return _stations; }
102
   const std::vector<Group>& getGroups() const { return _groups; }
107
   const std::vector<Group>& getGroups() const { return _groups; }
103
 
108
 
104
-
105
  private:
109
  private:
106
   void addStation(int64_t osmid, const util::geo::DLine& geom,
110
   void addStation(int64_t osmid, const util::geo::DLine& geom,
107
                   const util::geo::DLine& latLngs, size_t origGroup,
111
                   const util::geo::DLine& latLngs, size_t origGroup,
@@ -112,9 +116,12 @@ class StatIdx {
112
   void initGroups();
116
   void initGroups();
113
   void initSuggestions();
117
   void initSuggestions();
114
 
118
 
119
+  int nameAttrRel(const std::string& attr) const;
120
+
115
   util::geo::DLine getGroupArrow(size_t stat, const util::geo::DPoint& p) const;
121
   util::geo::DLine getGroupArrow(size_t stat, const util::geo::DPoint& p) const;
116
 
122
 
117
-  util::geo::Polygon<double> hull(const util::geo::MultiPoint<double>& imp, double rad) const;
123
+  util::geo::Polygon<double> hull(const util::geo::MultiPoint<double>& imp,
124
+                                  double rad) const;
118
 
125
 
119
   std::vector<Station> _stations;
126
   std::vector<Station> _stations;
120
   std::vector<Group> _groups;
127
   std::vector<Group> _groups;

+ 32 - 13
src/osmfixer/server/StatServer.cpp

@@ -2,9 +2,9 @@
2
 // Chair of Algorithms and Data Structures.
2
 // Chair of Algorithms and Data Structures.
3
 // Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
3
 // Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
4
 
4
 
5
-#include <vector>
6
 #include <codecvt>
5
 #include <codecvt>
7
 #include <locale>
6
 #include <locale>
7
+#include <vector>
8
 #include "osmfixer/build.h"
8
 #include "osmfixer/build.h"
9
 #include "osmfixer/index.h"
9
 #include "osmfixer/index.h"
10
 #include "osmfixer/server/StatServer.h"
10
 #include "osmfixer/server/StatServer.h"
@@ -36,6 +36,7 @@ util::http::Answer StatServer::handle(const util::http::Req& req,
36
           "200 OK", std::string(build_js, build_js + sizeof build_js /
36
           "200 OK", std::string(build_js, build_js + sizeof build_js /
37
                                                          sizeof build_js[0]));
37
                                                          sizeof build_js[0]));
38
       a.params["Content-Type"] = "application/javascript; charset=utf-8";
38
       a.params["Content-Type"] = "application/javascript; charset=utf-8";
39
+      a.params["Cache-Control"] = "public, max-age=10000";
39
     } else if (cmd == "/map") {
40
     } else if (cmd == "/map") {
40
       a = handleMapReq(params);
41
       a = handleMapReq(params);
41
     } else if (cmd == "/heatmap") {
42
     } else if (cmd == "/heatmap") {
@@ -211,10 +212,9 @@ util::http::Answer StatServer::handleMapReq(const Params& pars) const {
211
 
212
 
212
     const auto& gret = idx.getGroups(bbox);
213
     const auto& gret = idx.getGroups(bbox);
213
     for (const auto& group : gret) {
214
     for (const auto& group : gret) {
214
-      if (group->polyStations.size() == 1 && group->osmid < 2) continue;
215
       json << sep;
215
       json << sep;
216
       sep = ',';
216
       sep = ',';
217
-      printGroup(group, did, z < 15, &json);
217
+      printGroup(group, did, z < 15, false, &json);
218
     }
218
     }
219
   }
219
   }
220
 
220
 
@@ -295,7 +295,7 @@ void StatServer::printSugg(const Suggestion* sugg, size_t did,
295
 
295
 
296
 // _____________________________________________________________________________
296
 // _____________________________________________________________________________
297
 void StatServer::printGroup(const Group* group, size_t did, bool simple,
297
 void StatServer::printGroup(const Group* group, size_t did, bool simple,
298
-                            std::ostream* out) const {
298
+                            bool aggr, std::ostream* out) const {
299
   const auto& range = _idxs[did].first;
299
   const auto& range = _idxs[did].first;
300
 
300
 
301
   (*out) << "{\"i\":" << group->id + range.gidStart << ",\"g\":[";
301
   (*out) << "{\"i\":" << group->id + range.gidStart << ",\"g\":[";
@@ -310,11 +310,19 @@ void StatServer::printGroup(const Group* group, size_t did, bool simple,
310
     }
310
     }
311
   }
311
   }
312
   (*out) << "]";
312
   (*out) << "]";
313
-  if (group->osmid == 1) (*out) << ",\"n\":1";
313
+  bool sugg = group->osmid == 1 || group->suggestions.size() > 0;
314
+  int err = group->attrErrs.size();
315
+
316
+  if (aggr) {
317
+    for (const auto sid : group->polyStations) {
318
+      sugg = sugg || _idxs[did].second.getStation(sid)->suggestions.size();
319
+      err += _idxs[did].second.getStation(sid)->attrErrs.size();
320
+    }
321
+  }
314
 
322
 
315
-  if (group->suggestions.size() > 0) (*out) << ",\"s\":1";
323
+  if (sugg) (*out) << ",\"s\":1";
324
+  if (err) (*out) << ",\"e\":" << err;
316
 
325
 
317
-  if (group->attrErrs.size()) (*out) << ",\"e\":" << group->attrErrs.size();
318
   (*out) << "}";
326
   (*out) << "}";
319
 }
327
 }
320
 
328
 
@@ -375,8 +383,7 @@ size_t StatServer::getDidByGid(size_t gid) const {
375
 
383
 
376
 // _____________________________________________________________________________
384
 // _____________________________________________________________________________
377
 util::http::Answer StatServer::handleSearch(const Params& pars) const {
385
 util::http::Answer StatServer::handleSearch(const Params& pars) const {
378
-  if (pars.count("q") == 0)
379
-    throw std::invalid_argument("No query given.");
386
+  if (pars.count("q") == 0) throw std::invalid_argument("No query given.");
380
   std::string cb;
387
   std::string cb;
381
   if (pars.count("cb")) cb = pars.find("cb")->second.c_str();
388
   if (pars.count("cb")) cb = pars.find("cb")->second.c_str();
382
 
389
 
@@ -400,15 +407,27 @@ util::http::Answer StatServer::handleSearch(const Params& pars) const {
400
     const auto& idx = _idxs[did].second;
407
     const auto& idx = _idxs[did].second;
401
     size_t idxGid = gid - range.gidStart;
408
     size_t idxGid = gid - range.gidStart;
402
     const auto group = idx.getGroup(idxGid);
409
     const auto group = idx.getGroup(idxGid);
403
-    json << sep << "{\"gid\":" << gid << ",\"score\":" << res.second << ", \"name\":";
404
-    std::string name = group->uniqueNames.front();
410
+
411
+    bool asStat = group->polyStations.size() == 1;
412
+
413
+    json << sep << "{\"n\":";
414
+    std::string name = group->uniqueNames.front().second;
405
     json << "\"" << util::jsonStringEscape(name) << "\"";
415
     json << "\"" << util::jsonStringEscape(name) << "\"";
406
     std::string via = _searchIdx.getName(res.first.second);
416
     std::string via = _searchIdx.getName(res.first.second);
407
     if (name != via) {
417
     if (name != via) {
408
-      json << ",\"via\":" << "\"" << util::jsonStringEscape(via) << "\"";
418
+      json << ",\"v\":"
419
+           << "\"" << util::jsonStringEscape(via) << "\"";
409
     }
420
     }
410
 
421
 
411
-    json << ",\"latlng\":{\"lat\":" << group->centroid.getY() << ",\"lng\":" << group->centroid.getX() <<"}";
422
+    if (asStat) {
423
+      auto stat = idx.getStation(group->polyStations.front());
424
+      if (stat->osmid < 0) json << ",\"w\":1";
425
+      json << ",\"s\":";
426
+      printStation(stat, did, true, &json);
427
+    } else {
428
+      json << ",\"g\":";
429
+      printGroup(group, did, true, true, &json);
430
+    }
412
 
431
 
413
     sep = ",";
432
     sep = ",";
414
     json << "}";
433
     json << "}";

+ 1 - 5
src/osmfixer/server/StatServer.h

@@ -13,10 +13,6 @@
13
 
13
 
14
 namespace osmfixer {
14
 namespace osmfixer {
15
 
15
 
16
-struct IdRange {
17
-  size_t sidStart, sidEnd, gidStart, gidEnd, suggIdStart, suggIdEnd;
18
-};
19
-
20
 typedef std::map<std::string, std::string> Params;
16
 typedef std::map<std::string, std::string> Params;
21
 
17
 
22
 class StatServer : public util::http::Handler {
18
 class StatServer : public util::http::Handler {
@@ -38,7 +34,7 @@ class StatServer : public util::http::Handler {
38
   util::http::Answer handleSearch(const Params& pars) const;
34
   util::http::Answer handleSearch(const Params& pars) const;
39
 
35
 
40
   void printStation(const Station* stat, size_t did, bool simple, std::ostream* out) const;
36
   void printStation(const Station* stat, size_t did, bool simple, std::ostream* out) const;
41
-  void printGroup(const Group* stat, size_t did, bool simple, std::ostream* out) const;
37
+  void printGroup(const Group* stat, size_t did, bool simple, bool aggr, std::ostream* out) const;
42
   void printSugg(const Suggestion* stat, size_t did, std::ostream* out) const;
38
   void printSugg(const Suggestion* stat, size_t did, std::ostream* out) const;
43
 
39
 
44
   size_t getDidBySid(size_t sid) const;
40
   size_t getDidBySid(size_t sid) const;

BIN
web/closurec/compiler.jar


+ 7 - 0
web/codes.txt

@@ -0,0 +1,7 @@
1
+U+0061
2
+U+0074
3
+U+0073
4
+U+0079
5
+U+002B
6
+U+002D
7
+U+2212

File diff suppressed because it is too large
+ 506 - 46
web/index.html


File diff suppressed because it is too large
+ 5 - 0
web/leaflet.js


BIN
web/nunito-cyr-ext.woff2


BIN
web/nunito-cyr.woff2


BIN
web/nunito-lat-ext.woff2


BIN
web/nunito-lat-full.woff2


BIN
web/nunito-lat.woff2


BIN
web/nunito-viet.woff2


BIN
web/roboto.woff2


File diff suppressed because it is too large
+ 239 - 303
web/script.js