libosmscout 1.1.1
Loading...
Searching...
No Matches
Geometry.h
Go to the documentation of this file.
1#ifndef OSMSCOUT_UTIL_GEOMETRY_H
2#define OSMSCOUT_UTIL_GEOMETRY_H
3
4/*
5 This source is part of the libosmscout library
6 Copyright (C) 2009 Tim Teulings
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Lesser General Public
10 License as published by the Free Software Foundation; either
11 version 2.1 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21*/
22
23#include <algorithm>
24#include <array>
25#include <functional>
26#include <list>
27#include <unordered_map>
28#include <utility>
29#include <vector>
30#include <tuple>
31
33
37
38#include <osmscout/GeoCoord.h>
39#include <osmscout/Point.h>
41
45
46namespace osmscout {
47
54
55
61 inline double DegToRad(double deg)
62 {
63 return deg*M_PI/180.0;
64 }
65
71 inline double RadToDeg(double rad)
72 {
73 return rad*180.0/M_PI;
74 }
75
84 inline double AngleDiff(double a, double b)
85 {
86 double diff = std::abs(a - b);
87
88 if (diff > M_PI) {
89 return 2 * M_PI - diff;
90 }
91
92 return diff;
93 }
94
102 template< class InputIt >
103 void GetBoundingBox(const InputIt first,
104 const InputIt last,
105 GeoBox& boundingBox)
106 {
107 assert(first!=last);
108
109 double minLon=first->GetLon();
110 double maxLon=minLon;
111 double minLat=first->GetLat();
112 double maxLat=minLat;
113
114 for (InputIt i=first; i!=last; i++) {
115 minLon=std::min(minLon,i->GetLon());
116 maxLon=std::max(maxLon,i->GetLon());
117 minLat=std::min(minLat,i->GetLat());
118 maxLat=std::max(maxLat,i->GetLat());
119 }
120
121 boundingBox.Set(GeoCoord(minLat,minLon),
122 GeoCoord(maxLat,maxLon));
123 }
124
134 template<typename N>
135 GeoBox GetBoundingBox(const std::vector<N>& nodes)
136 {
137 assert(!nodes.empty());
138
139 GeoBox boundingBox;
140
141 for (const auto& node : nodes) {
142 boundingBox.Include(node);
143 }
144
145 return boundingBox;
146 }
147
157 inline GeoBox GetBoundingBox(const std::vector<Point>& nodes)
158 {
159 assert(!nodes.empty());
160
161 GeoBox boundingBox;
162
163 for (const auto& node : nodes) {
164 boundingBox.Include(node.GetCoord());
165 }
166
167 return boundingBox;
168 }
169
179 template<typename N>
180 void GetBoundingBox(const std::vector<N>& nodes,
181 GeoBox& boundingBox)
182 {
183 assert(!nodes.empty());
184
185 boundingBox.Invalidate();
186 for (const auto& node : nodes) {
187 boundingBox.Include(node);
188 }
189 }
190
199 inline void GetBoundingBox(const std::vector<Point>& nodes,
200 GeoBox& boundingBox)
201 {
202 assert(!nodes.empty());
203
204 boundingBox.Invalidate();
205 for (const auto& node : nodes) {
206 boundingBox.Include(node.GetCoord());
207 }
208 }
209
215 template<typename N>
216 bool LinesIntersect(const N& a1,
217 const N& a2,
218 const N& b1,
219 const N& b2)
220 {
221 if (a1.IsEqual(b1) ||
222 a1.IsEqual(b2) ||
223 a2.IsEqual(b1) ||
224 a2.IsEqual(b2)) {
225 return true;
226 }
227 if (a1.IsEqual(a2) &&
228 b1.IsEqual(b2)){
229 // two different zero size vectors can't intersects
230 return false;
231 }
232
233 // denominator, see GetLineIntersection for more details
234 double denr=(b2.GetLat()-b1.GetLat())*(a2.GetLon()-a1.GetLon())-
235 (b2.GetLon()-b1.GetLon())*(a2.GetLat()-a1.GetLat());
236
237 double ua_numr=(b2.GetLon()-b1.GetLon())*(a1.GetLat()-b1.GetLat())-
238 (b2.GetLat()-b1.GetLat())*(a1.GetLon()-b1.GetLon());
239 double ub_numr=(a2.GetLon()-a1.GetLon())*(a1.GetLat()-b1.GetLat())-
240 (a2.GetLat()-a1.GetLat())*(a1.GetLon()-b1.GetLon());
241
242 if (denr==0.0) { // parallels
243 if (ua_numr==0.0 && ub_numr==0.0) {
244 // two lines are on one straight line, check the bounds
245 GeoBox aBox(GeoCoord(a1.GetLat(),a1.GetLon()),
246 GeoCoord(a2.GetLat(),a2.GetLon()));
247 GeoBox bBox(GeoCoord(b1.GetLat(),b1.GetLon()),
248 GeoCoord(b2.GetLat(),b2.GetLon()));
249
250 return bBox.Includes(a1,false) ||
251 bBox.Includes(a2,false) ||
252 aBox.Includes(b1,false) ||
253 aBox.Includes(b2,false);
254 }
255
256 return false;
257 }
258
259 double ua=ua_numr/denr;
260 double ub=ub_numr/denr;
261
262 return ua>=0.0 &&
263 ua<=1.0 &&
264 ub>=0.0 &&
265 ub<=1.0;
266 }
267
273 template<typename N,typename I>
274 bool GetLineIntersection(const N& a1,
275 const N& a2,
276 const N& b1,
277 const N& b2,
278 I& intersection)
279 {
280 if (a1.IsEqual(b1) ||
281 a1.IsEqual(b2)){
282 intersection.Set(a1.GetLat(),a1.GetLon());
283 return true;
284 }
285 if (a2.IsEqual(b1) ||
286 a2.IsEqual(b2)) {
287 intersection.Set(a2.GetLat(),a2.GetLon());
288 return true;
289 }
290 if (a1.IsEqual(a2) &&
291 b1.IsEqual(b2)){
292 // two different zero size vectors can't intersects
293 return false;
294 }
295
296 // for geographic, expect point.x=lon and point.y=lat
297
298 // see https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
299 // and http://www.geeksforgeeks.org/orientation-3-ordered-points/
300
301 // denominator < 0 : left angle
302 // denominator == 0 : parallels
303 // denominator > 0 : right angle
304 double denr=(b2.GetLat()-b1.GetLat())*(a2.GetLon()-a1.GetLon())-
305 (b2.GetLon()-b1.GetLon())*(a2.GetLat()-a1.GetLat());
306
307 double ua_numr=(b2.GetLon()-b1.GetLon())*(a1.GetLat()-b1.GetLat())-
308 (b2.GetLat()-b1.GetLat())*(a1.GetLon()-b1.GetLon());
309 double ub_numr=(a2.GetLon()-a1.GetLon())*(a1.GetLat()-b1.GetLat())-
310 (a2.GetLat()-a1.GetLat())*(a1.GetLon()-b1.GetLon());
311
312 if (denr==0.0) {
313 if (ua_numr==0.0 && ub_numr==0.0) {
314 // two lines are on one straight line, check the bounds
315 GeoBox aBox(GeoCoord(a1.GetLat(),a1.GetLon()),
316 GeoCoord(a2.GetLat(),a2.GetLon()));
317 GeoBox bBox(GeoCoord(b1.GetLat(),b1.GetLon()),
318 GeoCoord(b2.GetLat(),b2.GetLon()));
319 if (bBox.Includes(a1,false)){
320 intersection.Set(a1.GetLat(),a1.GetLon());
321 return true;
322 }
323 if (bBox.Includes(a2,false)){
324 intersection.Set(a2.GetLat(),a2.GetLon());
325 return true;
326 }
327 if (aBox.Includes(b1,false)){
328 intersection.Set(b1.GetLat(),b1.GetLon());
329 return true;
330 }
331 if (aBox.Includes(b2,false)){
332 intersection.Set(b2.GetLat(),b2.GetLon());
333 return true;
334 }
335
336 return false;
337 }
338
339 return false;
340 }
341
342 double ua=ua_numr/denr;
343 double ub=ub_numr/denr;
344
345 if (ua>=0.0 &&
346 ua<=1.0 &&
347 ub>=0.0 &&
348 ub<=1.0) {
349 intersection.Set(a1.GetLat()+ua*(a2.GetLat()-a1.GetLat()),
350 a1.GetLon()+ua*(a2.GetLon()-a1.GetLon()));
351 return true;
352 }
353
354 return false;
355 }
356
362 template<typename N>
363 bool GetLineIntersectionPixel(const N& a1,
364 const N& a2,
365 const N& b1,
366 const N& b2,
367 N& intersection)
368 {
369 if (a1.IsEqual(b1) ||
370 a1.IsEqual(b2)){
371 intersection.Set(a1.GetX(),a1.GetY());
372 return true;
373 }
374 if (a2.IsEqual(b1) ||
375 a2.IsEqual(b2)) {
376 intersection.Set(a2.GetX(),a2.GetY());
377 return true;
378 }
379 if (a1.IsEqual(a2) &&
380 b1.IsEqual(b2)){
381 // two different zero size vectors can't intersects
382 return false;
383 }
384
385 double denr=(b2.GetY()-b1.GetY())*(a2.GetX()-a1.GetX())-
386 (b2.GetX()-b1.GetX())*(a2.GetY()-a1.GetY());
387
388 double ua_numr=(b2.GetX()-b1.GetX())*(a1.GetY()-b1.GetY())-
389 (b2.GetY()-b1.GetY())*(a1.GetX()-b1.GetX());
390 double ub_numr=(a2.GetX()-a1.GetX())*(a1.GetY()-b1.GetY())-
391 (a2.GetY()-a1.GetY())*(a1.GetX()-b1.GetX());
392
393 if (denr==0.0) {
394 // This gives currently false hits because of number resolution problems, if two lines are very
395 // close together and for example are part of a very details node curve intersections are detected.
396
397 // FIXME: setup intersection
398 return ua_numr==0.0 && ub_numr==0.0;
399 }
400
401 double ua=ua_numr/denr;
402 double ub=ub_numr/denr;
403
404 if (ua>=0.0 &&
405 ua<=1.0 &&
406 ub>=0.0 &&
407 ub<=1.0) {
408 intersection.Set(a1.GetX()+ua*(a2.GetX()-a1.GetX()),
409 a1.GetY()+ua*(a2.GetY()-a1.GetY()));
410 return true;
411 }
412
413 return false;
414 }
415
416 template<typename N>
417 double DistanceSquare(const N& a,
418 const N& b)
419 {
420 double lonDelta=a.GetLon()-b.GetLon();
421 double latDelta=a.GetLat()-b.GetLat();
422
423 return lonDelta*lonDelta+latDelta*latDelta;
424 }
425
433/*
434 template<typename N, typename M>
435 inline double IsLeft(const N& p0,
436 const N& p1,
437 const M& p2)
438 {
439 if ((p2.GetLat()==p0.GetLat() &&
440 p2.GetLon()==p0.GetLon()) ||
441 (p2.GetLat()==p1.GetLat() &&
442 p2.GetLon()==p1.GetLon())) {
443 return 0;
444 }
445
446 return (p1.GetLon()-p0.GetLon())*(p2.GetLat()-p0.GetLat())-
447 (p2.GetLon()-p0.GetLon())*(p1.GetLat()-p0.GetLat());
448 }
449
450 template<typename N, typename M>
451 inline bool IsCoordInArea(const N& point,
452 const std::vector<M>& nodes)
453 {
454 for (int i=0; i<(int)nodes.size()-1; i++) {
455 if (point.GetLat()==nodes[i].GetLat() &&
456 point.GetLon()==nodes[i].GetLon()) {
457 return true;
458 }
459 }
460
461 int wn=0; // the winding number counter
462
463 // loop through all edges of the polygon
464 for (int i=0; i<(int)nodes.size()-1; i++) { // edge from V[i] to V[i+1]
465 if (nodes[i].GetLat()<=point.GetLat()) { // start y <= P.y
466 if (nodes[i+1].GetLat()>point.GetLat()) { // an upward crossing
467 if (IsLeft(nodes[i],nodes[i+1],point) > 0) { // P left of edge
468 ++wn; // have a valid up intersect
469 }
470 }
471 }
472 else { // start y > P.y (no test needed)
473 if (nodes[i+1].GetLat()<=point.GetLat()) { // a downward crossing
474 if (IsLeft(nodes[i],nodes[i+1],point) < 0) { // P right of edge
475 --wn; // have a valid down intersect
476 }
477 }
478 }
479 }
480
481 return wn!=0;
482 }*/
483
491 template<typename N, typename M>
492 inline bool IsCoordInArea(const N& point,
493 const std::vector<M>& nodes)
494 {
495 size_t i;
496 size_t j;
497 bool c=false;
498
499 for (i=0, j=nodes.size()-1; i<nodes.size(); j=i++) {
500 if (point.GetLat()==nodes[i].GetLat() &&
501 point.GetLon()==nodes[i].GetLon()) {
502 return true;
503 }
504
505 if ((((nodes[i].GetLat()<=point.GetLat()) && (point.GetLat()<nodes[j].GetLat())) ||
506 ((nodes[j].GetLat()<=point.GetLat()) && (point.GetLat()<nodes[i].GetLat()))) &&
507 (point.GetLon()<(nodes[j].GetLon()-nodes[i].GetLon())*(point.GetLat()-nodes[i].GetLat())/(nodes[j].GetLat()-nodes[i].GetLat())+
508 nodes[i].GetLon())) {
509 c=!c;
510 }
511 }
512
513 return c;
514 }
515
523 template<typename N,typename M>
524 inline int GetRelationOfPointToArea(const N& point,
525 const std::vector<M>& nodes)
526 {
527 size_t i;
528 size_t j;
529 bool c=false;
530
531 for (i=0, j=nodes.size()-1; i<nodes.size(); j=i++) {
532 if (point.GetLat()==nodes[i].GetLat() &&
533 point.GetLon()==nodes[i].GetLon()) {
534 return 0;
535 }
536
537 if ((((nodes[i].GetLat()<=point.GetLat()) && (point.GetLat()<nodes[j].GetLat())) ||
538 ((nodes[j].GetLat()<=point.GetLat()) && (point.GetLat()<nodes[i].GetLat()))) &&
539 (point.GetLon()<(nodes[j].GetLon()-nodes[i].GetLon())*(point.GetLat()-nodes[i].GetLat())/(nodes[j].GetLat()-nodes[i].GetLat())+
540 nodes[i].GetLon())) {
541 c=!c;
542 }
543 }
544
545 return c ? 1 : -1;
546 }
547
552 template<typename N,typename M>
553 inline bool IsAreaCompletelyInArea(const std::vector<N>& a,
554 const std::vector<M>& b)
555 {
556 for (const auto& node : a) {
557 if (GetRelationOfPointToArea(node,b)<0) {
558 return false;
559 }
560 }
561
562 return true;
563 }
564
569 template<typename N,typename M>
570 inline bool IsAreaAtLeastPartlyInArea(const std::vector<N>& a,
571 const std::vector<M>& b,
572 const GeoBox& aBox,
573 const GeoBox& bBox)
574 {
575 if (!aBox.Intersects(bBox)){
576 return false;
577 }
578
579 for (const auto& node : a) {
580 if (bBox.Includes(node, /*openInterval*/ false) && GetRelationOfPointToArea(node,b)>=0) {
581 return true;
582 }
583 }
584
585 return false;
586 }
587
592 template<typename N,typename M>
593 inline bool IsAreaAtLeastPartlyInArea(const std::vector<N>& a,
594 const std::vector<M>& b)
595 {
596 GeoBox aBox;
597 GeoBox bBox;
598 GetBoundingBox(a, aBox);
599 GetBoundingBox(b, bBox);
600
601 return IsAreaAtLeastPartlyInArea(a,b,aBox,bBox);
602 }
603
612 template<typename N,typename M>
613 inline bool IsAreaSubOfArea(const std::vector<N>& a,
614 const std::vector<M>& b)
615 {
616 for (const auto& node : a) {
617 int relPos=GetRelationOfPointToArea(node,b);
618
619 if (relPos>0) {
620 return true;
621 }
622
623 if (relPos<0) {
624 return false;
625 }
626 }
627
628 return false;
629 }
630
639 template<typename N,typename M>
640 inline bool IsAreaSubOfAreaQuorum(const std::vector<N>& a,
641 const std::vector<M>& b)
642 {
643 size_t pro=0;
644 size_t contra=0;
645 size_t count=0;
646
647 for (const auto& node : a) {
648 int relPos=GetRelationOfPointToArea(node,b);
649
650 ++count;
651
652 if (relPos>0) {
653 ++pro;
654 }
655 else if (relPos<0) {
656 ++contra;
657 }
658
659 if (count>=100 && double(pro)/20.0>double(contra)) {
660 return true;
661 }
662 }
663
664 return double(pro)/20.0>double(contra);
665 }
666
676 template<typename N,typename M>
677 inline bool IsAreaSubOfAreaOrSame(const std::vector<N>& a,
678 const std::vector<M>& b)
679 {
680 size_t pro=0;
681 size_t contra=0;
682 size_t count=0;
683
684 pro=0;
685 contra=0;
686 count=0;
687
688 for (const auto& node : a) {
689 int relPos=GetRelationOfPointToArea(node,b);
690
691 ++count;
692
693 if (relPos>0) {
694 ++pro;
695 }
696 else if (relPos<0) {
697 ++contra;
698 }
699
700 if (count>=100 && pro/20>contra) {
701 return true;
702 }
703 }
704
705 if (pro == 0 && contra == 0 && count > 0) {
706 return true;
707 }
708
709 return pro/20.0>contra;
710 }
711
721 template<typename N>
722 bool AreaIsSimple(std::vector<N> points)
723 {
724 if (points.size()<3) {
725 return false;
726 }
727
728 points.push_back(points[0]);
729
730 size_t edgesIntersect=0;
731
732 for (size_t i=0; i<points.size()-1; i++) {
733 edgesIntersect=0;
734
735 for (size_t j=i+1; j<points.size()-1; j++) {
736 if (LinesIntersect(points[i],
737 points[i+1],
738 points[j],
739 points[j+1])) {
740 edgesIntersect++;
741
742 if (i==0) {
743 if (edgesIntersect>2) {
744 return false;
745 }
746 }
747 else {
748 if (edgesIntersect>1) {
749 return false;
750 }
751 }
752 }
753 }
754 }
755
756 return true;
757 }
758
768 template<typename N>
769 bool AreaIsSimple(const std::vector<std::pair<N,N> >& edges,
770 const std::vector<bool>& edgeStartsNewPoly)
771 {
772 size_t edgesIntersect=0;
773
774 for (size_t i=0; i<edges.size(); i++) {
775 edgesIntersect=0;
776
777 for (size_t j=i+1; j<edges.size(); j++) {
778 if (LinesIntersect(edges[i].first,
779 edges[i].second,
780 edges[j].first,
781 edges[j].second)) {
782 edgesIntersect++;
783
784 // we check the first edge of a sole polygon against every
785 // other edge and expect to see 2 intersections for
786 // adjacent edges; polygon is complex if there are more
787 // intersections
788 if (edgeStartsNewPoly[i]) {
789 if (edgesIntersect>2) {
790 return false;
791 }
792 }
793
794 // otherwise we check an edge that isn't the first
795 // edge against every other edge excluding those that
796 // have already been tested (this means one adjacent
797 // edge); polygon is complex if there is more than one
798 // intersection
799 else {
800 if (edgesIntersect>1) {
801 return false;
802 }
803 }
804 }
805 }
806 }
807
808 return true;
809 }
810
815 template<typename N>
816 bool AreaIsCCW(const std::vector<N> &edges)
817 {
818 // based on http://en.wikipedia.org/wiki/Curve_orientation
819 // and http://local.wasp.uwa.edu.au/~pbourke/geometry/clockwise/
820
821 // note: polygon must be simple
822 // note: this assumes 2d cartesian coordinate space is used;
823 // for geographic, expect Vec2.x=lon and Vec2.y=lat!
824
825 size_t ptIdx=0;
826
827 for (size_t i=1; i<edges.size(); i++) {
828 // find the point with the smallest y value,
829 if (edges[i].y<edges[ptIdx].y) {
830 ptIdx=i;
831 }
832 // if y values are equal save the point with greatest x
833 else if (edges[i].y==edges[ptIdx].y) {
834 if (edges[i].x<edges[ptIdx].x) {
835 ptIdx=i;
836 }
837 }
838 }
839
840 size_t prevIdx=(ptIdx==0) ? edges.size()-1 : ptIdx-1;
841 size_t nextIdx=(ptIdx==edges.size()-1) ? 0 : ptIdx+1;
842
843 double signedArea=(edges[ptIdx].x-edges[prevIdx].x)*
844 (edges[nextIdx].y-edges[ptIdx].y)-
845 (edges[ptIdx].y-edges[prevIdx].y)*
846 (edges[nextIdx].x-edges[ptIdx].x);
847
848 return signedArea>0.0;
849 }
850
858 template<typename N>
859 bool AreaIsValid(std::vector<N>& outerPoints,
860 std::vector<std::vector<N> >& innerPoints)
861 {
862 if (outerPoints.size()<3) {
863 return false;
864 }
865
866 size_t numEdges=outerPoints.size();
867
868 for (size_t i=0; i<innerPoints.size(); i++) {
869 numEdges+=innerPoints[i].size();
870 }
871
872 std::vector<bool> edgeStartsNewPoly(numEdges,false);
873 std::vector<std::pair<N,N> > listEdges(numEdges);
874 size_t cEdge=0;
875
876 // temporarily wrap around vertices
877 // (first == last) to generate edge lists
878 outerPoints.push_back(outerPoints[0]);
879 for (size_t i=0; i<innerPoints.size(); i++) {
880 innerPoints[i].push_back(innerPoints[i][0]);
881 }
882
883 // outer poly
884 edgeStartsNewPoly[0]=true;
885 for (size_t i=1; i<outerPoints.size(); i++) {
886 std::pair<N, N> outerEdge;
887
888 outerEdge.first=outerPoints[i-1];
889 outerEdge.second=outerPoints[i];
890 listEdges[cEdge]=outerEdge;
891
892 cEdge++;
893 }
894
895 // inner polys
896 for (size_t i=0; i<innerPoints.size(); i++) {
897 edgeStartsNewPoly[cEdge]=true;
898
899 for (size_t j=1; j<innerPoints[i].size(); j++) {
900 std::pair<N, N> innerEdge;
901
902 innerEdge.first=innerPoints[i][j-1];
903 innerEdge.second=innerPoints[i][j];
904 listEdges[cEdge]=innerEdge;
905
906 cEdge++;
907 }
908 }
909
910 // revert vertex list modifications (not
911 // really the 'nicest' way of doing this)
912 outerPoints.pop_back();
913 for (size_t i=0; i<innerPoints.size(); i++) {
914 innerPoints[i].pop_back();
915 }
916
917 if (AreaIsSimple(listEdges,edgeStartsNewPoly))
918 {
919 // expect listOuterPts to be CCW and innerPts
920 // to be CW, if not then reverse point order
921
922 if (!AreaIsCCW(outerPoints)) {
923 std::reverse(outerPoints.begin(),
924 outerPoints.end());
925 }
926
927 for (int i=0; i<innerPoints.size(); i++) {
928 if (AreaIsCCW(innerPoints[i])) {
929 std::reverse(innerPoints[i].begin(),
930 innerPoints[i].end());
931 }
932 }
933 }
934 else {
935 return false;
936 }
937
938 return true;
939 }
940
947 inline void Normalize(double x,
948 double y,
949 double& nx,
950 double& ny)
951 {
952 double length=sqrt(x*x+y*y);
953
954 if (length!=0) {
955 nx=x/length;
956 ny=y/length;
957 }
958 }
959
964 inline double Det(double x1,
965 double y1,
966 double x2,
967 double y2)
968 {
969 return x1*y2-y1*x2;
970 }
971
972
977 extern OSMSCOUT_API size_t Pow(size_t a, size_t b);
978
987 template<typename N>
988 bool AreaIsClockwise(const std::vector<N>& edges)
989 {
990 assert(edges.size()>=3);
991 // based on http://en.wikipedia.org/wiki/Curve_orientation
992 // and http://local.wasp.uwa.edu.au/~pbourke/geometry/clockwise/
993
994 // note: polygon must be simple
995
996 size_t ptIdx=0;
997
998 for (size_t i=1; i<edges.size(); i++) {
999 // find the point with the smallest y value,
1000 if (edges[i].GetLat()<edges[ptIdx].GetLat()) {
1001 ptIdx=i;
1002 }
1003 // if y values are equal save the point with greatest x
1004 else if (edges[i].GetLat()==edges[ptIdx].GetLat()) {
1005 if (edges[i].GetLon()<edges[ptIdx].GetLon()) {
1006 ptIdx=i;
1007 }
1008 }
1009 }
1010
1011 size_t prevIdx=(ptIdx==0) ? edges.size()-1 : ptIdx-1;
1012 size_t nextIdx=(ptIdx==edges.size()-1) ? 0 : ptIdx+1;
1013
1014 double signedArea=(edges[ptIdx].GetLon()-edges[prevIdx].GetLon())*
1015 (edges[nextIdx].GetLat()-edges[ptIdx].GetLat())-
1016 (edges[ptIdx].GetLat()-edges[prevIdx].GetLat())*
1017 (edges[nextIdx].GetLon()-edges[ptIdx].GetLon());
1018
1019 return signedArea<0.0;
1020 }
1021
1022
1034 template<typename N>
1036 const N& a,
1037 const N& b)
1038 {
1039 double xdelta=b.GetLon()-a.GetLon();
1040 double ydelta=b.GetLat()-a.GetLat();
1041
1042 if (xdelta==0 && ydelta==0) {
1043 return std::numeric_limits<double>::infinity();
1044 }
1045
1046 double u=((p.GetLon()-a.GetLon())*xdelta+(p.GetLat()-a.GetLat())*ydelta)/(xdelta*xdelta+ydelta*ydelta);
1047
1048 double cx;
1049 double cy;
1050
1051 if (u<0) {
1052 cx=a.GetLon();
1053 cy=a.GetLat();
1054 }
1055 else if (u>1) {
1056 cx=b.GetLon();
1057 cy=b.GetLat();
1058 }
1059 else {
1060 cx=a.GetLon()+u*xdelta;
1061 cy=a.GetLat()+u*ydelta;
1062 }
1063
1064 double dx=cx-p.GetLon();
1065 double dy=cy-p.GetLat();
1066
1067 return sqrt(dx*dx+dy*dy);
1068 }
1069
1070 extern OSMSCOUT_API double CalculateDistancePointToLineSegment(const GeoCoord& p,
1071 const GeoCoord& a,
1072 const GeoCoord& b,
1073 GeoCoord& intersection);
1074
1080 extern OSMSCOUT_API Distance GetSphericalDistance(const GeoCoord& a,
1081 const GeoCoord& b);
1082
1088 extern OSMSCOUT_API Distance GetEllipsoidalDistance(double aLon, double aLat,
1089 double bLon, double bLat);
1090
1096 extern OSMSCOUT_API Distance GetEllipsoidalDistance(const GeoCoord& a,
1097 const GeoCoord& b);
1098
1104 extern OSMSCOUT_API void GetEllipsoidalDistance(double lat1, double lon1,
1105 const Bearing &bearing,
1106 const Distance &distance,
1107 double& lat2, double& lon2);
1108
1114 extern OSMSCOUT_API GeoCoord GetEllipsoidalDistance(const GeoCoord& position,
1115 const Bearing &bearing,
1116 const Distance &distance);
1117
1123 extern OSMSCOUT_API Bearing GetSphericalBearingInitial(const GeoCoord& a,
1124 const GeoCoord& b);
1125
1131 extern OSMSCOUT_API Bearing GetSphericalBearingFinal(const GeoCoord& a,
1132 const GeoCoord& b);
1133
1141 extern OSMSCOUT_API double GetDistanceInLonDegrees(const Distance &d,
1142 double latitude=0);
1143
1148 extern OSMSCOUT_API double NormalizeRelativeAngle(double angle);
1149
1151 {
1152 int x;
1153 int y;
1154
1155 ScanCell(int x, int y);
1156
1157 void Set(int x, int y)
1158 {
1159 this->x=x;
1160 this->y=y;
1161 }
1162
1163 bool operator==(const ScanCell& other) const
1164 {
1165 return x==other.x && y==other.y;
1166 }
1167
1168 bool operator<(const ScanCell& other) const
1169 {
1170 return std::tie(y, x) < std::tie(other.y, other.x);
1171 }
1172
1173 bool IsEqual(const ScanCell& other) const
1174 {
1175 return x==other.x && y==other.y;
1176 }
1177
1178 int GetX() const
1179 {
1180 return x;
1181 }
1182
1183 int GetY() const
1184 {
1185 return y;
1186 }
1187 };
1188
1190 {
1191 size_t from;
1192 size_t to;
1193 GeoBox bbox;
1194 };
1195
1200 void OSMSCOUT_API ScanConvertLine(int x1, int y1,
1201 int x2, int y2,
1202 std::vector<ScanCell>& cells);
1203
1210 extern OSMSCOUT_API double DistanceToSegment(double px, double py,
1211 double p1x, double p1y,
1212 double p2x, double p2y,
1213 double& r,
1214 double& qx, double& qy);
1215
1216 extern OSMSCOUT_API double DistanceToSegment(const GeoCoord& point,
1217 const GeoCoord& segmentStart,
1218 const GeoCoord& segmentEnd,
1219 double& r,
1220 GeoCoord& intersection);
1221
1222 extern OSMSCOUT_API double DistanceToSegment(const std::vector<Point>& points,
1223 const GeoCoord& segmentStart,
1224 const GeoCoord& segmentEnd,
1225 GeoCoord& location,
1226 GeoCoord& intersection);
1227
1228 extern OSMSCOUT_API double DistanceToSegment(const GeoBox& boundingBox,
1229 const GeoCoord& segmentStart,
1230 const GeoCoord& segmentEnd,
1231 GeoCoord& location,
1232 GeoCoord& intersection);
1233
1239 {
1240 GeoCoord point;
1241 size_t aIndex;
1242 size_t bIndex;
1247 };
1248
1255 template<typename N>
1256 void GetSegmentBoundingBox(const std::vector<N>& path,
1257 size_t from, size_t to,
1258 GeoBox& boundingBox)
1259 {
1260 if (path.empty() || from>=to){
1261 boundingBox.Invalidate();
1262 return;
1263 }
1264
1265 double minLon=path[from%path.size()].GetLon();
1266 double maxLon=minLon;
1267 double minLat=path[from%path.size()].GetLat();
1268 double maxLat=minLat;
1269
1270 for (size_t i=from; i<to; i++) {
1271 minLon=std::min(minLon,path[i%path.size()].GetLon());
1272 maxLon=std::max(maxLon,path[i%path.size()].GetLon());
1273 minLat=std::min(minLat,path[i%path.size()].GetLat());
1274 maxLat=std::max(maxLat,path[i%path.size()].GetLat());
1275 }
1276
1277 boundingBox.Set(GeoCoord(minLat,minLon),
1278 GeoCoord(maxLat,maxLon));
1279 }
1280
1287 template<typename N>
1288 void ComputeSegmentBoxes(const std::vector<N>& path,
1289 std::vector<SegmentGeoBox> &segmentBoxes,
1290 size_t bound,
1291 size_t segmentSize = 1000)
1292 {
1293 assert(segmentSize>0);
1294 for (size_t i=0;i<bound;i+=segmentSize){
1295 SegmentGeoBox box;
1296 box.from = i;
1297 box.to = std::min(i+segmentSize,bound);
1298 GetSegmentBoundingBox(path, box.from, box.to, box.bbox);
1299 segmentBoxes.push_back(box);
1300 }
1301 }
1302
1309 template<typename N>
1310 void FindPathIntersections(const std::vector<N> &aPath,
1311 const std::vector<N> &bPath,
1312 bool aClosed,
1313 bool bClosed,
1314 std::vector<PathIntersection> &intersections,
1315 size_t aStartIndex=0,
1316 size_t bStartIndex=0)
1317 {
1318 size_t aIndex=aStartIndex;
1319 size_t bIndex=bStartIndex;
1320 // bounds are inclusive
1321 size_t aBound=aClosed?aIndex+aPath.size():aPath.size()-1;
1322 size_t bBound=bClosed?bIndex+bPath.size():bPath.size()-1;
1323 size_t bStart=bIndex;
1324
1325 if (bStart>=bBound || aIndex>=aBound){
1326 return;
1327 }
1328
1329 GeoBox bBox;
1330 GetSegmentBoundingBox(bPath,bIndex,bBound+1,bBox);
1331 GeoBox aLineBox;
1332 GeoBox bLineBox;
1333
1334 // compute b-boxes for B path, each 1000 point-long segment
1335 std::vector<SegmentGeoBox> bSegmentBoxes;
1336 ComputeSegmentBoxes(bPath,bSegmentBoxes,bBound+1, 1000);
1337
1338 for (;aIndex<aBound;aIndex++){
1339 N a1=aPath[aIndex%aPath.size()];
1340 N a2=aPath[(aIndex+1)%aPath.size()];
1341 aLineBox.Set(GeoCoord(a1.GetLat(),a1.GetLon()),
1342 GeoCoord(a2.GetLat(),a2.GetLon()));
1343 if (!bBox.Intersects(aLineBox,/*openInterval*/false)){
1344 continue;
1345 }
1346 for (bIndex=bStart;bIndex<bBound;bIndex++){
1347 N b1=bPath[bIndex%bPath.size()];
1348 N b2=bPath[(bIndex+1)%bPath.size()];
1349 bLineBox.Set(GeoCoord(b1.GetLat(),b1.GetLon()),
1350 GeoCoord(b2.GetLat(),b2.GetLon()));
1351
1352 if (!bSegmentBoxes[bIndex/1000].bbox.Intersects(aLineBox,/*openInterval*/false) &&
1353 !aLineBox.Intersects(bLineBox,/*openInterval*/false)){
1354 // round up
1355 bIndex+=std::max(0, 998-(int)(bIndex%1000));
1356 continue;
1357 }
1358 if (!aLineBox.Intersects(bLineBox,/*openInterval*/false)){
1359 continue;
1360 }
1362 if (GetLineIntersection(a1,a2,
1363 b1,b2,
1364 pi.point)){
1365
1366 // if b2==pi or a1==pi, orientation is zero then
1367 // for that reason, we prolong both lines for computation
1368 // of intersection orientation
1369 GeoCoord pointBefore(a1.GetLat()-(a2.GetLat()-a1.GetLat()),
1370 a1.GetLon()-(a2.GetLon()-a1.GetLon()));
1371 GeoCoord pointAfter(b2.GetLat()+(b2.GetLat()-b1.GetLat()),
1372 b2.GetLon()+(b2.GetLon()-b1.GetLon()));
1373 pi.orientation=(pi.point.GetLon()-pointBefore.GetLon())*
1374 (pointAfter.GetLat()-pi.point.GetLat())-
1375 (pi.point.GetLat()-pointBefore.GetLat())*
1376 (pointAfter.GetLon()-pi.point.GetLon());
1377
1378 pi.aIndex=aIndex;
1379 pi.bIndex=bIndex;
1380 pi.aDistanceSquare=DistanceSquare(GeoCoord(a1.GetLat(),a1.GetLon()),pi.point);
1381 pi.bDistanceSquare=DistanceSquare(GeoCoord(b1.GetLat(),b1.GetLon()),pi.point);
1382
1383 intersections.push_back(pi);
1384 }
1385 }
1386 }
1387 }
1388
1395 template<typename N>
1396 bool FindIntersection(const std::vector<N> &way,
1397 size_t &i,
1398 size_t &j)
1399 {
1400 GeoBox lineBox;
1401
1402 // compute b-boxes for path, each 1000 point-long segment
1403 std::vector<SegmentGeoBox> segmentBoxes;
1404 ComputeSegmentBoxes(way,segmentBoxes,way.size(),1000);
1405
1406 for (; i<way.size()-1; i++) {
1407 N i1=way[i];
1408 N i2=way[i+1];
1409 lineBox.Set(GeoCoord(i1.GetLat(),i1.GetLon()),
1410 GeoCoord(i2.GetLat(),i2.GetLon()));
1411
1412 for (j=i+1; j<way.size()-1; j++) {
1413 if (!segmentBoxes[j/1000].bbox.Intersects(lineBox,/*openInterval*/false) &&
1414 !segmentBoxes[(j+1)/1000].bbox.Intersects(lineBox,/*openInterval*/false)){
1415 // round up
1416 j+=std::max(0, 998-(int)(j%1000));
1417 continue;
1418 }
1419 if (LinesIntersect(way[i],
1420 way[i+1],
1421 way[j],
1422 way[j+1])) {
1423
1424 if (way[i].IsEqual(way[j]) ||
1425 way[i].IsEqual(way[j+1]) ||
1426 way[i+1].IsEqual(way[j]) ||
1427 way[i+1].IsEqual(way[j+1])) {
1428 continue; // ignore sibling edges
1429 }
1430
1431 return true;
1432 }
1433 }
1434 }
1435 return false;
1436 }
1437
1439 {
1440 private:
1441 struct Edge
1442 {
1443 size_t fromIndex;
1444 size_t toIndex;
1445 };
1446
1447 public:
1448 struct Polygon
1449 {
1450 std::vector<Point> coords;
1451 };
1452
1453 private:
1454 std::unordered_map<Id,size_t> nodeIdIndexMap;
1455 std::vector<Point> nodes;
1456 std::list<Edge> edges;
1457
1458 private:
1459 void RemoveEliminatingEdges();
1460
1461 public:
1462 void AddPolygon(const std::vector<Point>& polygonsCoords);
1463
1464 bool Merge(std::list<Polygon>& result);
1465 };
1466
1468 {
1469 double width;
1470 double height;
1471 };
1472
1473 const size_t CELL_DIMENSION_MAX = 25;
1475
1476 extern OSMSCOUT_API const std::array<CellDimension,CELL_DIMENSION_COUNT> cellDimension;
1477
1488 {
1489 public:
1490 enum class Direction
1491 {
1492 HORIZONTAL,
1493 VERTICAL
1494 };
1495
1496 private:
1497 GeoBox box;
1498 Direction direction;
1499 double parts;
1500 size_t currentIndex=0;
1501 GeoBox currentBox;
1502
1503 private:
1504 void CalculateBox();
1505
1506 public:
1507 GeoBoxPartitioner(const GeoBox& box,
1508 Direction direction,
1509 size_t parts)
1510 : box(box),
1511 direction(direction),
1512 parts((double)parts)
1513 {
1514 assert(currentIndex<parts);
1515 CalculateBox();
1516 }
1517
1518 void Advance()
1519 {
1520 currentIndex++;
1521
1522 if (currentIndex<parts) {
1523 CalculateBox();
1524 }
1525 }
1526
1527 GeoBox GetCurrentGeoBox() const
1528 {
1529 assert(currentIndex<parts);
1530 return currentBox;
1531 }
1532
1533 size_t GetCurrentIndex() const
1534 {
1535 assert(currentIndex<parts);
1536 return currentIndex;
1537 }
1538 };
1539}
1540
1541namespace std {
1542 template <>
1543 struct hash<osmscout::ScanCell>
1544 {
1545 size_t operator()(const osmscout::ScanCell& cell) const
1546 {
1547 return hash<int>{}(cell.GetX()) ^ (hash<int>{}(cell.GetY()) << 1);
1548 }
1549 };
1550}
1551
1552#endif
#define OSMSCOUT_API
Definition CoreImportExport.h:45
GeoBoxPartitioner(const GeoBox &box, Direction direction, size_t parts)
Definition Geometry.h:1507
size_t GetCurrentIndex() const
Definition Geometry.h:1533
void Advance()
Definition Geometry.h:1518
GeoBox GetCurrentGeoBox() const
Definition Geometry.h:1527
Direction
Definition Geometry.h:1491
Definition Geometry.h:1439
bool Merge(std::list< Polygon > &result)
void AddPolygon(const std::vector< Point > &polygonsCoords)
bool GetLineIntersectionPixel(const N &a1, const N &a2, const N &b1, const N &b2, N &intersection)
Definition Geometry.h:363
bool FindIntersection(const std::vector< N > &way, size_t &i, size_t &j)
Definition Geometry.h:1396
double Det(double x1, double y1, double x2, double y2)
Definition Geometry.h:964
void OSMSCOUT_API ScanConvertLine(int x1, int y1, int x2, int y2, std::vector< ScanCell > &cells)
OSMSCOUT_API double DistanceToSegment(double px, double py, double p1x, double p1y, double p2x, double p2y, double &r, double &qx, double &qy)
OSMSCOUT_API Bearing GetSphericalBearingFinal(const GeoCoord &a, const GeoCoord &b)
OSMSCOUT_API Distance GetEllipsoidalDistance(double aLon, double aLat, double bLon, double bLat)
double AngleDiff(double a, double b)
Definition Geometry.h:84
OSMSCOUT_API Bearing GetSphericalBearingInitial(const GeoCoord &a, const GeoCoord &b)
int GetRelationOfPointToArea(const N &point, const std::vector< M > &nodes)
Definition Geometry.h:524
bool IsCoordInArea(const N &point, const std::vector< M > &nodes)
Definition Geometry.h:492
bool IsAreaSubOfAreaQuorum(const std::vector< N > &a, const std::vector< M > &b)
Definition Geometry.h:640
bool AreaIsClockwise(const std::vector< N > &edges)
Definition Geometry.h:988
bool GetLineIntersection(const N &a1, const N &a2, const N &b1, const N &b2, I &intersection)
Definition Geometry.h:274
bool AreaIsValid(std::vector< N > &outerPoints, std::vector< std::vector< N > > &innerPoints)
Definition Geometry.h:859
bool IsAreaCompletelyInArea(const std::vector< N > &a, const std::vector< M > &b)
Definition Geometry.h:553
bool AreaIsCCW(const std::vector< N > &edges)
Definition Geometry.h:816
double RadToDeg(double rad)
Definition Geometry.h:71
bool IsAreaSubOfAreaOrSame(const std::vector< N > &a, const std::vector< M > &b)
Definition Geometry.h:677
OSMSCOUT_API Distance GetSphericalDistance(const GeoCoord &a, const GeoCoord &b)
double DegToRad(double deg)
Definition Geometry.h:61
bool AreaIsSimple(std::vector< N > points)
Definition Geometry.h:722
void FindPathIntersections(const std::vector< N > &aPath, const std::vector< N > &bPath, bool aClosed, bool bClosed, std::vector< PathIntersection > &intersections, size_t aStartIndex=0, size_t bStartIndex=0)
Definition Geometry.h:1310
OSMSCOUT_API double GetDistanceInLonDegrees(const Distance &d, double latitude=0)
void GetBoundingBox(const InputIt first, const InputIt last, GeoBox &boundingBox)
Definition Geometry.h:103
bool LinesIntersect(const N &a1, const N &a2, const N &b1, const N &b2)
Definition Geometry.h:216
void Normalize(double x, double y, double &nx, double &ny)
Definition Geometry.h:947
bool IsAreaSubOfArea(const std::vector< N > &a, const std::vector< M > &b)
Definition Geometry.h:613
bool IsAreaAtLeastPartlyInArea(const std::vector< N > &a, const std::vector< M > &b, const GeoBox &aBox, const GeoBox &bBox)
Definition Geometry.h:570
OSMSCOUT_API double NormalizeRelativeAngle(double angle)
Definition Area.h:39
const size_t CELL_DIMENSION_COUNT
Definition Geometry.h:1474
OSMSCOUT_API const std::array< CellDimension, CELL_DIMENSION_COUNT > cellDimension
OSMSCOUT_API size_t Pow(size_t a, size_t b)
double DistanceSquare(const N &a, const N &b)
Definition Geometry.h:417
void ComputeSegmentBoxes(const std::vector< N > &path, std::vector< SegmentGeoBox > &segmentBoxes, size_t bound, size_t segmentSize=1000)
Definition Geometry.h:1288
void GetSegmentBoundingBox(const std::vector< N > &path, size_t from, size_t to, GeoBox &boundingBox)
Definition Geometry.h:1256
double CalculateDistancePointToLineSegment(const N &p, const N &a, const N &b)
Definition Geometry.h:1035
const size_t CELL_DIMENSION_MAX
Definition Geometry.h:1473
STL namespace.
Definition Geometry.h:1468
double width
Definition Geometry.h:1469
double height
Definition Geometry.h:1470
Definition Geometry.h:1239
double aDistanceSquare
distance^2 between "a path" point and intersection
Definition Geometry.h:1245
GeoCoord point
intersection point
Definition Geometry.h:1240
size_t bIndex
"b path" point index before intersection
Definition Geometry.h:1242
double orientation
Definition Geometry.h:1243
size_t aIndex
"a path" point index before intersection
Definition Geometry.h:1241
double bDistanceSquare
distance^2 between "b path" point and intersection
Definition Geometry.h:1246
Definition Geometry.h:1449
std::vector< Point > coords
Definition Geometry.h:1450
Definition Geometry.h:1151
int x
Definition Geometry.h:1152
int y
Definition Geometry.h:1153
ScanCell(int x, int y)
bool operator==(const ScanCell &other) const
Definition Geometry.h:1163
void Set(int x, int y)
Definition Geometry.h:1157
bool IsEqual(const ScanCell &other) const
Definition Geometry.h:1173
int GetX() const
Definition Geometry.h:1178
int GetY() const
Definition Geometry.h:1183
bool operator<(const ScanCell &other) const
Definition Geometry.h:1168
Definition Geometry.h:1190
GeoBox bbox
Definition Geometry.h:1193
size_t to
exclusive
Definition Geometry.h:1192
size_t from
Definition Geometry.h:1191
size_t operator()(const osmscout::ScanCell &cell) const
Definition Geometry.h:1545