Patrick Brosi 5 years ago
parent
commit
cdc8fef405

+ 195 - 0
.ycm_extra_conf.py

@@ -0,0 +1,195 @@
1
+# This file is NOT licensed under the GPLv3, which is the license for the rest
2
+# of YouCompleteMe.
3
+#
4
+# Here's the license text for this file:
5
+#
6
+# This is free and unencumbered software released into the public domain.
7
+#
8
+# Anyone is free to copy, modify, publish, use, compile, sell, or
9
+# distribute this software, either in source code form or as a compiled
10
+# binary, for any purpose, commercial or non-commercial, and by any
11
+# means.
12
+#
13
+# In jurisdictions that recognize copyright laws, the author or authors
14
+# of this software dedicate any and all copyright interest in the
15
+# software to the public domain. We make this dedication for the benefit
16
+# of the public at large and to the detriment of our heirs and
17
+# successors. We intend this dedication to be an overt act of
18
+# relinquishment in perpetuity of all present and future rights to this
19
+# software under copyright law.
20
+#
21
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24
+# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
25
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
26
+# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27
+# OTHER DEALINGS IN THE SOFTWARE.
28
+#
29
+# For more information, please refer to <http://unlicense.org/>
30
+
31
+import os
32
+import ycm_core
33
+
34
+# These are the compilation flags that will be used in case there's no
35
+# compilation database set (by default, one is not set).
36
+# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR.
37
+flags = [
38
+'-Wall',
39
+'-Wextra',
40
+'-Werror',
41
+'-Wno-c++98-compat',
42
+'-Wno-long-long',
43
+'-Wno-variadic-macros',
44
+'-fexceptions',
45
+'-DNDEBUG',
46
+# You 100% do NOT need -DUSE_CLANG_COMPLETER in your flags; only the YCM
47
+# source code needs it.
48
+'-DUSE_CLANG_COMPLETER',
49
+# THIS IS IMPORTANT! Without a "-std=<something>" flag, clang won't know which
50
+# language to use when compiling headers. So it will guess. Badly. So C++
51
+# headers will be compiled as C headers. You don't want that so ALWAYS specify
52
+# a "-std=<something>".
53
+# For a C project, you would set this to something like 'c99' instead of
54
+# 'c++11'.
55
+'-std=c++11',
56
+# ...and the same thing goes for the magic -x option which specifies the
57
+# language that the files to be compiled are written in. This is mostly
58
+# relevant for c++ headers.
59
+# For a C project, you would set this to 'c' instead of 'c++'.
60
+'-x',
61
+'c++',
62
+'-isystem',
63
+'../BoostParts',
64
+'-isystem',
65
+# This path will only work on OS X, but extra paths that don't exist are not
66
+# harmful
67
+'/System/Library/Frameworks/Python.framework/Headers',
68
+'-isystem',
69
+'../llvm/include',
70
+'-isystem',
71
+'../llvm/tools/clang/include',
72
+'-I',
73
+'.',
74
+'-I',
75
+'./ClangCompleter',
76
+'-isystem',
77
+'./tests/gmock/gtest',
78
+'-isystem',
79
+'./tests/gmock/gtest/include',
80
+'-isystem',
81
+'./tests/gmock',
82
+'-isystem',
83
+'./tests/gmock/include'
84
+'-I',
85
+'/usr/lib/gcc/x86_64-linux-gnu/4.8/include/',
86
+'-I',
87
+'/usr/lib/gcc/x86_64-linux-gnu/5/include/',
88
+'-I',
89
+'/usr/lib/gcc/x86_64-linux-gnu/6/include/',
90
+'-fopenmp'
91
+]
92
+
93
+
94
+# Set this to the absolute path to the folder (NOT the file!) containing the
95
+# compile_commands.json file to use that instead of 'flags'. See here for
96
+# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
97
+#
98
+# You can get CMake to generate this file for you by adding:
99
+#   set( CMAKE_EXPORT_COMPILE_COMMANDS 1 )
100
+# to your CMakeLists.txt file.
101
+#
102
+# Most projects will NOT need to set this to anything; you can just change the
103
+# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
104
+compilation_database_folder = 'build'
105
+
106
+if os.path.exists( compilation_database_folder ):
107
+  database = ycm_core.CompilationDatabase( compilation_database_folder )
108
+else:
109
+  database = None
110
+
111
+SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ]
112
+
113
+def DirectoryOfThisScript():
114
+  return os.path.dirname( os.path.abspath( __file__ ) )
115
+
116
+
117
+def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
118
+  if not working_directory:
119
+    return list( flags )
120
+  new_flags = []
121
+  make_next_absolute = False
122
+  path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
123
+  for flag in flags:
124
+    new_flag = flag
125
+
126
+    if make_next_absolute:
127
+      make_next_absolute = False
128
+      if not flag.startswith( '/' ):
129
+        new_flag = os.path.join( working_directory, flag )
130
+
131
+    for path_flag in path_flags:
132
+      if flag == path_flag:
133
+        make_next_absolute = True
134
+        break
135
+
136
+      if flag.startswith( path_flag ):
137
+        path = flag[ len( path_flag ): ]
138
+        new_flag = path_flag + os.path.join( working_directory, path )
139
+        break
140
+
141
+    if new_flag:
142
+      new_flags.append( new_flag )
143
+  return new_flags
144
+
145
+
146
+def IsHeaderFile( filename ):
147
+  extension = os.path.splitext( filename )[ 1 ]
148
+  return extension in [ '.h', '.hxx', '.hpp', '.hh' ]
149
+
150
+
151
+def GetCompilationInfoForFile( filename ):
152
+  # The compilation_commands.json file generated by CMake does not have entries
153
+  # for header files. So we do our best by asking the db for flags for a
154
+  # corresponding source file, if any. If one exists, the flags for that file
155
+  # should be good enough.
156
+  if IsHeaderFile( filename ):
157
+    basename = os.path.splitext( filename )[ 0 ]
158
+    for extension in SOURCE_EXTENSIONS:
159
+      replacement_file = basename + extension
160
+      if os.path.exists( replacement_file ):
161
+        compilation_info = database.GetCompilationInfoForFile(
162
+          replacement_file )
163
+        if compilation_info.compiler_flags_:
164
+          return compilation_info
165
+    return None
166
+  return database.GetCompilationInfoForFile( filename )
167
+
168
+
169
+def FlagsForFile( filename, **kwargs ):
170
+  if database:
171
+    # Bear in mind that compilation_info.compiler_flags_ does NOT return a
172
+    # python list, but a "list-like" StringVec object
173
+    compilation_info = GetCompilationInfoForFile( filename )
174
+    if not compilation_info:
175
+      return None
176
+
177
+    final_flags = MakeRelativePathsInFlagsAbsolute(
178
+      compilation_info.compiler_flags_,
179
+      compilation_info.compiler_working_dir_ )
180
+
181
+    # NOTE: This is just for YouCompleteMe; it's highly likely that your project
182
+    # does NOT need to remove the stdlib flag. DO NOT USE THIS IN YOUR
183
+    # ycm_extra_conf IF YOU'RE NOT 100% SURE YOU NEED IT.
184
+    try:
185
+      final_flags.remove( '-stdlib=libc++' )
186
+    except ValueError:
187
+      pass
188
+  else:
189
+    relative_to = DirectoryOfThisScript()
190
+    final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )
191
+
192
+  return {
193
+    'flags': final_flags,
194
+    'do_cache': True
195
+  }

+ 87 - 10
src/osmfixer/index/StatIdx.cpp

@@ -12,6 +12,7 @@
12 12
 using osmfixer::StatIdx;
13 13
 using osmfixer::OsmAttrs;
14 14
 using osmfixer::Station;
15
+using osmfixer::Group;
15 16
 
16 17
 // _____________________________________________________________________________
17 18
 void StatIdx::readFromFile(const std::string& path) {
@@ -25,41 +26,117 @@ void StatIdx::readFromFile(const std::string& path) {
25 26
     if (line.size() == 0) break;
26 27
 
27 28
     std::stringstream rec(line);
28
-    size_t osmid;
29
+    size_t osmid, group;
29 30
     double lat, lng;
30 31
     OsmAttrs attrs;
31 32
 
32 33
     rec >> osmid;
33 34
     rec >> lat;
34 35
     rec >> lng;
36
+    rec >> group;
35 37
 
36
-    addStation(osmid, lat, lng, attrs);
38
+    addStation(osmid, lat, lng, group, attrs);
37 39
   }
38 40
 
39
-  initIndex();
41
+  // second, parse groups
42
+  while (std::getline(f, line)) {
43
+    // empty line is separator between blocks
44
+    if (line.size() == 0) break;
45
+
46
+    std::stringstream rec(line);
47
+    size_t osmid;
48
+    OsmAttrs attrs;
49
+
50
+    rec >> osmid;
40 51
 
41
-  std::cout << util::geo::getWKT(_bbox) << std::endl;
52
+    addGroup(osmid, attrs);
53
+  }
54
+
55
+  initGroups();
56
+  initIndex();
42 57
 }
43 58
 
44 59
 // _____________________________________________________________________________
45
-void StatIdx::addStation(size_t osmid, double lat, double lng,
60
+void StatIdx::addStation(size_t osmid, double lat, double lng, size_t group,
46 61
                          const OsmAttrs& attrs) {
47 62
   std::cout << "Record: " << osmid << " " << lat << " " << lng << std::endl;
48 63
   auto point = util::geo::latLngToWebMerc<double>(lat, lng);
49 64
 
50
-  _stations.emplace_back(Station{osmid, point, attrs});
65
+  _stations.emplace_back(Station{osmid, group, point, attrs});
51 66
 
52 67
   // extend bounding box
53 68
   _bbox = util::geo::extendBox(point, _bbox);
54 69
 }
55 70
 
56 71
 // _____________________________________________________________________________
72
+void StatIdx::addGroup(size_t osmid, const OsmAttrs& attrs) {
73
+  std::cout << osmid << std::endl;
74
+  Group g;
75
+  g.osmid = osmid;
76
+  g.attrs = attrs;
77
+  _groups.emplace_back(g);
78
+}
79
+
80
+// _____________________________________________________________________________
81
+void StatIdx::initGroups() {
82
+  for (size_t i = 0; i < _stations.size(); i++) {
83
+    // this should be ensured by the input file
84
+    assert(_stations[i].group < _groups.size());
85
+    _groups[_stations[i].group].stations.push_back(i);
86
+  }
87
+
88
+  for (size_t i = 0; i < _groups.size(); i++) {
89
+    util::geo::MultiPoint<double> mp;
90
+    for (size_t stid : _groups[i].stations) {
91
+      double rad = 11.0;
92
+      int n = 50.0;
93
+      for (int i = 0; i <= n; i++) {
94
+        double x = rad * cos((2.0 * M_PI / static_cast<double>(n)) *
95
+                             static_cast<double>(i));
96
+        double y = rad * sin((2.0 * M_PI / static_cast<double>(n)) *
97
+                             static_cast<double>(i));
98
+
99
+        mp.push_back(util::geo::DPoint(_stations[stid].pos.getX() + x,
100
+                                       _stations[stid].pos.getY() + y));
101
+      }
102
+    }
103
+    _groups[i].poly = util::geo::convexHull(mp);
104
+  }
105
+}
106
+
107
+// _____________________________________________________________________________
57 108
 void StatIdx::initIndex() {
58
-  _grid = util::geo::Grid<size_t, util::geo::Point, double>(5000, 5000, _bbox,
59
-                                                            false);
109
+  _sgrid = util::geo::Grid<size_t, util::geo::Point, double>(5000, 5000, _bbox,
110
+                                                             false);
111
+  _ggrid = util::geo::Grid<size_t, util::geo::Polygon, double>(5000, 5000,
112
+                                                               _bbox, false);
60 113
   for (size_t i = 0; i < _stations.size(); i++) {
61
-    _grid.add(_stations[i].pos, i);
114
+    _sgrid.add(_stations[i].pos, i);
115
+  }
116
+
117
+  for (size_t i = 0; i < _groups.size(); i++) {
118
+    _ggrid.add(_groups[i].poly, i);
119
+  }
120
+}
121
+
122
+// _____________________________________________________________________________
123
+std::vector<const Group*> StatIdx::getGroups(const util::geo::DBox bbox) const {
124
+  std::vector<const Group*> ret;
125
+  auto ll = util::geo::latLngToWebMerc<double>(bbox.getLowerLeft().getX(),
126
+                                               bbox.getLowerLeft().getY());
127
+  auto ur = util::geo::latLngToWebMerc<double>(bbox.getUpperRight().getX(),
128
+                                               bbox.getUpperRight().getY());
129
+
130
+  std::set<size_t> tmp;
131
+  auto reqBox = util::geo::DBox(ll, ur);
132
+  _ggrid.get(reqBox, &tmp);
133
+
134
+  for (auto i : tmp) {
135
+    if (util::geo::intersects(_groups[i].poly, reqBox))
136
+      ret.push_back(&_groups[i]);
62 137
   }
138
+
139
+  return ret;
63 140
 }
64 141
 
65 142
 // _____________________________________________________________________________
@@ -73,7 +150,7 @@ std::vector<const Station*> StatIdx::getStations(
73 150
 
74 151
   std::set<size_t> tmp;
75 152
   auto reqBox = util::geo::DBox(ll, ur);
76
-  _grid.get(reqBox, &tmp);
153
+  _sgrid.get(reqBox, &tmp);
77 154
 
78 155
   for (auto i : tmp) {
79 156
     if (util::geo::contains(_stations[i].pos, reqBox))

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

@@ -17,10 +17,18 @@ typedef std::map<std::string, std::vector<std::string>> OsmAttrs;
17 17
 
18 18
 struct Station {
19 19
   size_t osmid;
20
+  size_t group;
20 21
   util::geo::DPoint pos;
21 22
   OsmAttrs attrs;
22 23
 };
23 24
 
25
+struct Group {
26
+  size_t osmid;
27
+  util::geo::DPolygon poly;
28
+  OsmAttrs attrs;
29
+  std::vector<size_t> stations;
30
+};
31
+
24 32
 class StatIdx {
25 33
  public:
26 34
   StatIdx() {}
@@ -28,17 +36,23 @@ class StatIdx {
28 36
   void readFromFile(const std::string& path);
29 37
 
30 38
   std::vector<const Station*> getStations(const util::geo::DBox bbox) const;
39
+  std::vector<const Group*> getGroups(const util::geo::DBox bbox) const;
31 40
 
32 41
  private:
33
-  void addStation(size_t id, double lat, double lng, const OsmAttrs& attrs);
42
+  void addStation(size_t id, double lat, double lng, size_t group,
43
+                  const OsmAttrs& attrs);
44
+  void addGroup(size_t id, const OsmAttrs& attrs);
34 45
 
35 46
   void initIndex();
47
+  void initGroups();
36 48
 
37 49
   std::vector<Station> _stations;
50
+  std::vector<Group> _groups;
38 51
   util::geo::DBox _bbox;
39 52
 
40
-  util::geo::Grid<size_t, util::geo::Point, double> _grid;
53
+  util::geo::Grid<size_t, util::geo::Point, double> _sgrid;
54
+  util::geo::Grid<size_t, util::geo::Polygon, double> _ggrid;
41 55
 };
42
-}
56
+}  // namespace osmfixer
43 57
 
44
-#endif
58
+#endif  // OSMFIXER_INDEX_STATIDX_H_

+ 49 - 7
src/osmfixer/server/StatServer.cpp

@@ -2,12 +2,12 @@
2 2
 // Chair of Algorithms and Data Structures.
3 3
 // Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
4 4
 
5
+#include <vector>
5 6
 #include "osmfixer/server/StatServer.h"
6 7
 #include "util/Misc.h"
7 8
 #include "util/String.h"
8 9
 #include "util/geo/Geo.h"
9 10
 #include "util/log/Log.h"
10
-#include "util/String.h"
11 11
 
12 12
 using osmfixer::StatServer;
13 13
 using osmfixer::Params;
@@ -51,8 +51,7 @@ util::http::Answer StatServer::handleMapReq(const Params& pars) const {
51 51
   if (pars.count("cb")) cb = pars.find("cb")->second.c_str();
52 52
   auto box = util::split(pars.find("bbox")->second, ',');
53 53
 
54
-  if (box.size() != 4)
55
-    throw std::invalid_argument("Invalid request.");
54
+  if (box.size() != 4) throw std::invalid_argument("Invalid request.");
56 55
 
57 56
   double lat1 = atof(box[0].c_str());
58 57
   double lng1 = atof(box[1].c_str());
@@ -61,18 +60,37 @@ util::http::Answer StatServer::handleMapReq(const Params& pars) const {
61 60
 
62 61
   std::cout << pars.find("bbox")->second << std::endl;
63 62
 
64
-  util::geo::DBox bbox(util::geo::DPoint(lng1, lat1), util::geo::DPoint(lng2, lat2));
63
+  util::geo::DBox bbox(util::geo::DPoint(lat1, lng1),
64
+                       util::geo::DPoint(lat2, lng2));
65 65
 
66 66
   LOG(INFO) << "Request for bounding box " << util::geo::getWKT(bbox);
67 67
 
68 68
   std::stringstream json;
69 69
 
70
+  json << std::setprecision(10);
71
+
70 72
   if (cb.size()) json << cb << "(";
71
-  json << "[";
73
+  json << "{\"stats\":[";
74
+
75
+  auto ret = _idx->getStations(bbox);
76
+  char sep = ' ';
77
+  for (auto stat : ret) {
78
+    json << sep;
79
+    sep = ',';
80
+    printStation(stat, &json);
81
+  }
72 82
 
73
-  // TODO: query index
83
+  json << "], \"groups\":[";
84
+  auto gret = _idx->getGroups(bbox);
85
+  sep = ' ';
86
+  for (auto group : gret) {
87
+    json << sep;
88
+    sep = ',';
89
+    printGroup(group, &json);
90
+  }
91
+
92
+  json << "]}";
74 93
 
75
-  json << "]";
76 94
   if (cb.size()) json << ")";
77 95
 
78 96
   auto answ = util::http::Answer("200 OK", json.str(), true);
@@ -82,6 +100,30 @@ util::http::Answer StatServer::handleMapReq(const Params& pars) const {
82 100
 }
83 101
 
84 102
 // _____________________________________________________________________________
103
+void StatServer::printStation(const Station* stat, std::ostream* out) {
104
+  auto latLng =
105
+      util::geo::webMercToLatLng<double>(stat->pos.getX(), stat->pos.getY());
106
+  (*out) << "{\"id\":" << stat->osmid << ",\"lat\":"
107
+         << latLng.getY() << ",\"lon\":" << latLng.getX() << "}";
108
+}
109
+
110
+// _____________________________________________________________________________
111
+void StatServer::printGroup(const Group* group, std::ostream* out) {
112
+  std::vector<util::geo::DPoint> projPoly;
113
+
114
+  for (auto p : group->poly.getOuter()) {
115
+    projPoly.push_back(util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
116
+  }
117
+  (*out) << "{\"id\":" << group->osmid << ",\"poly\":[";
118
+  char sep = ' ';
119
+  for (auto p : projPoly) {
120
+    (*out) << sep << "[" << p.getY() << "," << p.getX() << "]";
121
+    sep = ',';
122
+  }
123
+  (*out) << "]}";
124
+}
125
+
126
+// _____________________________________________________________________________
85 127
 std::string StatServer::parseUrl(std::string u, std::string pl,
86 128
                                  std::map<std::string, std::string>* params) {
87 129
   auto parts = util::split(u, '?');

+ 8 - 3
src/osmfixer/server/StatServer.h

@@ -5,6 +5,8 @@
5 5
 #ifndef OSMFIXER_SERVER_STATSERVER_H_
6 6
 #define OSMFIXER_SERVER_STATSERVER_H_
7 7
 
8
+#include <map>
9
+#include <string>
8 10
 #include "osmfixer/index/StatIdx.h"
9 11
 #include "util/http/Server.h"
10 12
 
@@ -14,7 +16,7 @@ typedef std::map<std::string, std::string> Params;
14 16
 
15 17
 class StatServer : public util::http::Handler {
16 18
  public:
17
-  StatServer(const osmfixer::StatIdx* idx) : _idx(idx) {}
19
+  explicit StatServer(const osmfixer::StatIdx* idx) : _idx(idx) {}
18 20
 
19 21
   virtual util::http::Answer handle(const util::http::Req& request,
20 22
                                     int connection) const;
@@ -24,8 +26,11 @@ class StatServer : public util::http::Handler {
24 26
 
25 27
   util::http::Answer handleMapReq(const Params& pars) const;
26 28
 
29
+  static void printStation(const Station* stat, std::ostream* out);
30
+  static void printGroup(const Group* stat, std::ostream* out);
31
+
27 32
   const osmfixer::StatIdx* _idx;
28 33
 };
29
-}
34
+}  // namespace osmfixer
30 35
 
31
-#endif
36
+#endif  // OSMFIXER_SERVER_STATSERVER_H_

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


+ 100 - 17
src/util/geo/Geo.h

@@ -243,15 +243,21 @@ inline bool doubleEq(double a, double b) { return fabs(a - b) < EPSILON; }
243 243
 // _____________________________________________________________________________
244 244
 template <typename T>
245 245
 inline bool contains(const Point<T>& p, const Box<T>& box) {
246
-  return p.getX() >= box.getLowerLeft().getX() &&
247
-         p.getX() <= box.getUpperRight().getX() &&
248
-         p.getY() >= box.getLowerLeft().getY() &&
249
-         p.getY() <= box.getUpperRight().getY();
246
+  // check if point lies in box
247
+  return (fabs(p.getX() - box.getLowerLeft().getX()) < EPSILON ||
248
+          p.getX() > box.getLowerLeft().getX()) &&
249
+         (fabs(p.getX() - box.getUpperRight().getX()) < EPSILON ||
250
+          p.getX() < box.getUpperRight().getX()) &&
251
+         (fabs(p.getY() - box.getLowerLeft().getY()) < EPSILON ||
252
+          p.getY() > box.getLowerLeft().getY()) &&
253
+         (fabs(p.getY() - box.getUpperRight().getY()) < EPSILON ||
254
+          p.getY() < box.getUpperRight().getY());
250 255
 }
251 256
 
252 257
 // _____________________________________________________________________________
253 258
 template <typename T>
254 259
 inline bool contains(const Line<T>& l, const Box<T>& box) {
260
+  // check if line lies in box
255 261
   for (const auto& p : l)
256 262
     if (!contains(p, box)) return false;
257 263
   return true;
@@ -260,24 +266,35 @@ inline bool contains(const Line<T>& l, const Box<T>& box) {
260 266
 // _____________________________________________________________________________
261 267
 template <typename T>
262 268
 inline bool contains(const LineSegment<T>& l, const Box<T>& box) {
269
+  // check if line segment lies in box
263 270
   return contains(l.first, box) && contains(l.second, box);
264 271
 }
265 272
 
266 273
 // _____________________________________________________________________________
267 274
 template <typename T>
268 275
 inline bool contains(const Box<T>& b, const Box<T>& box) {
276
+  // check if box b lies in box
269 277
   return contains(b.getLowerLeft(), box) && contains(b.getUpperRight(), box);
270 278
 }
271 279
 
272 280
 // _____________________________________________________________________________
273 281
 template <typename T>
274 282
 inline bool contains(const Point<T>& p, const LineSegment<T>& ls) {
283
+  // check if point p lies in (on) line segment ls
275 284
   return fabs(crossProd(p, ls)) < EPSILON && contains(p, getBoundingBox(ls));
276 285
 }
277 286
 
278 287
 // _____________________________________________________________________________
279 288
 template <typename T>
289
+inline bool contains(const LineSegment<T>& a, const LineSegment<T>& b) {
290
+  // check if line segment a is contained in line segment b
291
+  return contains(a.first, b) && contains(a.second, b);
292
+}
293
+
294
+// _____________________________________________________________________________
295
+template <typename T>
280 296
 inline bool contains(const Point<T>& p, const Line<T>& l) {
297
+  // check if point p lies in line l
281 298
   for (size_t i = 1; i < l.size(); i++) {
282 299
     if (contains(p, LineSegment<T>(l[i - 1], l[i]))) return true;
283 300
   }
@@ -287,6 +304,8 @@ inline bool contains(const Point<T>& p, const Line<T>& l) {
287 304
 // _____________________________________________________________________________
288 305
 template <typename T>
289 306
 inline bool contains(const Point<T>& p, const Polygon<T>& poly) {
307
+  // check if point p lies in polygon
308
+
290 309
   // see https://de.wikipedia.org/wiki/Punkt-in-Polygon-Test_nach_Jordan
291 310
   int8_t c = -1;
292 311
 
@@ -328,19 +347,53 @@ inline int8_t polyContCheck(const Point<T>& a, Point<T> b, Point<T> c) {
328 347
 // _____________________________________________________________________________
329 348
 template <typename T>
330 349
 inline bool contains(const Polygon<T>& polyC, const Polygon<T>& poly) {
331
-  for (const auto& p : polyC.getOuter()) {
332
-    if (!contains(p, poly)) {
350
+  // check if polygon polyC lies in polygon poly
351
+
352
+  for (size_t i = 1; i < polyC.getOuter().size(); i++) {
353
+    if (!contains(LineSegment<T>(polyC.getOuter()[i - 1], polyC.getOuter()[i]),
354
+                  poly))
355
+      return false;
356
+  }
357
+
358
+  // also check the last hop
359
+  if (!contains(LineSegment<T>(polyC.getOuter().back(), polyC.getOuter().front()),
360
+                poly))
361
+    return false;
362
+
363
+  return true;
364
+}
365
+
366
+// _____________________________________________________________________________
367
+template <typename T>
368
+inline bool contains(const LineSegment<T>& ls, const Polygon<T>& p) {
369
+  // check if linesegment ls lies in polygon poly
370
+
371
+  // if one of the endpoints lies outside, abort
372
+  if (!contains(ls.first, p)) return false;
373
+  if (!contains(ls.second, p)) return false;
374
+
375
+  for (size_t i = 1; i < p.getOuter().size(); i++) {
376
+    auto seg = LineSegment<T>(p.getOuter()[i - 1], p.getOuter()[i]);
377
+    if (!(contains(ls.first, seg) || contains(ls.second, seg)) &&
378
+        intersects(seg, ls)) {
333 379
       return false;
334 380
     }
335 381
   }
382
+
383
+  auto seg = LineSegment<T>(p.getOuter().back(), p.getOuter().front());
384
+  if (!(contains(ls.first, seg) || contains(ls.second, seg)) &&
385
+      intersects(seg, ls)) {
386
+    return false;
387
+  }
388
+
336 389
   return true;
337 390
 }
338 391
 
339 392
 // _____________________________________________________________________________
340 393
 template <typename T>
341 394
 inline bool contains(const Line<T>& l, const Polygon<T>& poly) {
342
-  for (const auto& p : l) {
343
-    if (!contains(p, poly)) {
395
+  for (size_t i = 1; i < l.size(); i++) {
396
+    if (!contains(LineSegment<T>(l[i - 1], l[i]), poly)) {
344 397
       return false;
345 398
     }
346 399
   }
@@ -351,9 +404,7 @@ inline bool contains(const Line<T>& l, const Polygon<T>& poly) {
351 404
 template <typename T>
352 405
 inline bool contains(const Line<T>& l, const Line<T>& other) {
353 406
   for (const auto& p : l) {
354
-    if (!contains(p, other)) {
355
-      return false;
356
-    }
407
+    if (!contains(p, other)) return false;
357 408
   }
358 409
   return true;
359 410
 }
@@ -361,17 +412,13 @@ inline bool contains(const Line<T>& l, const Line<T>& other) {
361 412
 // _____________________________________________________________________________
362 413
 template <typename T>
363 414
 inline bool contains(const Box<T>& b, const Polygon<T>& poly) {
364
-  return contains(b.getLowerLeft(), poly) &&
365
-         contains(b.getUpperRight(), poly) &&
366
-         contains(Point<T>(b.getUpperRight().getX(), b.getLowerLeft().getY()),
367
-                  poly) &&
368
-         contains(Point<T>(b.getLowerLeft().getX(), b.getUpperRight().getY()),
369
-                  poly);
415
+  return contains(convexHull(b), poly);
370 416
 }
371 417
 
372 418
 // _____________________________________________________________________________
373 419
 template <typename T>
374 420
 inline bool contains(const Polygon<T>& poly, const Box<T>& b) {
421
+  // check of poly lies in box
375 422
   for (const auto& p : poly.getOuter()) {
376 423
     if (!contains(p, b)) return false;
377 424
   }
@@ -400,6 +447,8 @@ inline bool contains(const std::vector<GeometryA<T>>& multigeo,
400 447
 // _____________________________________________________________________________
401 448
 template <typename T>
402 449
 inline bool intersects(const LineSegment<T>& ls1, const LineSegment<T>& ls2) {
450
+  // check if two linesegments intersect
451
+
403 452
   // two line segments intersect of there is a single, well-defined intersection
404 453
   // point between them. If more than 1 endpoint is colinear with any line,
405 454
   // the segments have infinite intersections. We handle this case as non-
@@ -470,6 +519,39 @@ inline bool intersects(const Box<T>& b1, const Box<T>& b2) {
470 519
 
471 520
 // _____________________________________________________________________________
472 521
 template <typename T>
522
+inline bool intersects(const Box<T>& b, const Polygon<T>& poly) {
523
+  return intersects(b, poly);
524
+}
525
+
526
+// _____________________________________________________________________________
527
+template <typename T>
528
+inline bool intersects(const Polygon<T>& poly, const Box<T>& b) {
529
+  if (intersects(
530
+          LineSegment<T>(b.getLowerLeft(), Point<T>(b.getUpperRight().getX(),
531
+                                                    b.getLowerLeft().getY())),
532
+          poly))
533
+    return true;
534
+  if (intersects(
535
+          LineSegment<T>(b.getLowerLeft(), Point<T>(b.getLowerLeft().getX(),
536
+                                                    b.getUpperRight().getY())),
537
+          poly))
538
+    return true;
539
+  if (intersects(
540
+          LineSegment<T>(b.getUpperRight(), Point<T>(b.getLowerLeft().getX(),
541
+                                                     b.getUpperRight().getY())),
542
+          poly))
543
+    return true;
544
+  if (intersects(
545
+          LineSegment<T>(b.getUpperRight(), Point<T>(b.getUpperRight().getX(),
546
+                                                     b.getLowerLeft().getY())),
547
+          poly))
548
+    return true;
549
+
550
+  return contains(poly, b) || contains(b, poly);
551
+}
552
+
553
+// _____________________________________________________________________________
554
+template <typename T>
473 555
 inline bool intersects(const LineSegment<T>& ls, const Box<T>& b) {
474 556
   if (intersects(ls, LineSegment<T>(b.getLowerLeft(),
475 557
                                     Point<T>(b.getUpperRight().getX(),
@@ -499,6 +581,7 @@ inline bool intersects(const LineSegment<T>& ls, const Polygon<T>& p) {
499 581
       return true;
500 582
   }
501 583
 
584
+  // also check the last hop
502 585
   if (intersects(LineSegment<T>(p.getOuter().back(), p.getOuter().front()), ls))
503 586
     return true;
504 587
 

+ 20 - 0
src/util/tests/TestMain.cpp

@@ -1446,6 +1446,26 @@ CASE("geometry") {
1446 1446
   EXPECT(geo::dist(Line<double>{{7, 7}, {7, -7}, {-7, -7}, {-7, 7}, {9, 0}, {-9, 0}, {0, 9}, {0, -9}}, Point<double>{7, 4}) == approx(0));
1447 1447
   EXPECT(geo::dist(Line<double>{{0, 0}, {1, 1}, {2, 0}}, Line<double>{{1.5, 0.5}, {1.5, 100}}) == approx(0));
1448 1448
   EXPECT(geo::dist(Line<double>{{0, 0}, {1, 1}, {2, 0}}, Line<double>{{2, 0.5}, {2, 100}}) == approx(0.353553));
1449
+
1450
+  EXPECT(geo::contains(util::geo::Point<double>{1.5, 0.5}, util::geo::LineSegment<double>{{1, 1},{1.5, 0.5}}));
1451
+  EXPECT(geo::contains(util::geo::Point<double>{1.5, 0.5}, util::geo::LineSegment<double>{{1, 1},{1.5, 0.5}}));
1452
+
1453
+  auto polyTest = geo::Polygon<double>({{1, 1}, {3, 1}, {2, 2}, {3, 3}, {1, 3}});
1454
+  EXPECT(!geo::contains(util::geo::LineSegment<double>({2.5, 1.3}, {2.5, 2.6}), polyTest));
1455
+
1456
+  EXPECT(!geo::contains(util::geo::LineSegment<double>{{2.5, 1.3}, {2.5, 2.6}}, polyTest));
1457
+  EXPECT(geo::contains(util::geo::LineSegment<double>{{2.5, 2.6}, {1.5, 2}}, polyTest));
1458
+  EXPECT(!geo::contains(util::geo::Line<double>{{2.5, 1.3}, {2.5, 2.6}, {1.5, 2}}, polyTest));
1459
+  EXPECT(geo::contains(util::geo::Line<double>{{2.5, 1.3}, {1.5, 2}, {2.5, 2.6}}, polyTest));
1460
+
1461
+  EXPECT(!geo::contains(util::geo::Box<double>{{1, 1},{2.5, 2.6}}, polyTest));
1462
+
1463
+  EXPECT(geo::intersects(Box<double>(Point<double>(0, 0), Point<double>(10, 10)), Box<double>(Point<double>(2, 2), Point<double>(8, 8))));
1464
+  EXPECT(geo::intersects(Box<double>(Point<double>(0, 0), Point<double>(10, 10)), Box<double>(Point<double>(-2, -2), Point<double>(8, 8))));
1465
+  EXPECT(geo::intersects(Box<double>(Point<double>(0, 0), Point<double>(10, 10)), Box<double>(Point<double>(-2, -2), Point<double>(12, 12))));
1466
+  EXPECT(geo::intersects(Box<double>(Point<double>(0, 0), Point<double>(10, 10)), Box<double>(Point<double>(5, 5), Point<double>(12, 12))));
1467
+
1468
+  EXPECT(!geo::intersects(Box<double>(Point<double>(0, 0), Point<double>(10, 10)), Box<double>(Point<double>(15, 15), Point<double>(12, 12))));
1449 1469
 }
1450 1470
 
1451 1471
 }};