source: XIOS/trunk/extern/remap/src/intersection_ym.cpp @ 1580

Last change on this file since 1580 was 1580, checked in by ymipsl, 6 years ago

Computation of the area of a polygon on the sphere :
The total area was obtain by triangulation of vertex with the barycenter of the polygons. for some pathological polygones, baricenter was outside or triangle side was cutting polygons side, leading to numerical imprecision.

Now the polygon is triangulate with by external lib (earcut), and correct area is computed in any case.

YM

File size: 8.6 KB
Line 
1#include "intersection_ym.hpp"
2#include "elt.hpp"
3#include "clipper.hpp"
4#include "gridRemap.hpp"
5#include "triple.hpp"
6#include "polyg.hpp"
7#include <vector>
8#include <stdlib.h>
9#include <limits>
10#include <array>
11#include <cstdint>
12#include "earcut.hpp"
13#include <fstream>
14
15
16#define epsilon 1e-3  // epsilon distance ratio over side lenght for approximate small circle by great circle
17#define fusion_vertex 1e-13
18
19namespace sphereRemap {
20
21using namespace std;
22using namespace ClipperLib ;
23       
24
25double intersect_ym(Elt *a, Elt *b)
26{
27
28  using N = uint32_t;
29  using Point = array<double, 2>;
30  vector<Point> vect_points;
31  vector< vector<Point> > polyline;
32
33// transform small circle into piece of great circle if necessary
34
35  vector<Coord> srcPolygon ;
36  createGreatCirclePolygon(*b, srcGrid.pole, srcPolygon) ;
37//  b->area=polygonarea(&srcPolygon[0],srcPolygon.size()) ;
38  vector<Coord> dstPolygon ;
39  createGreatCirclePolygon(*a, tgtGrid.pole, dstPolygon) ;
40  a->area=polygonarea(&dstPolygon[0],dstPolygon.size()) ; // just for target
41
42// compute coordinates of the polygons into the gnomonique plane tangent to barycenter C of dst polygon
43// transform system coordinate : Z axis along OC
44  int na=dstPolygon.size() ;
45  Coord *a_gno   = new Coord[na];
46  int nb=srcPolygon.size() ;
47  Coord *b_gno   = new Coord[nb];
48
49  Coord OC=barycentre(a->vertex,a->n) ;
50  Coord Oz=OC ;
51  Coord Ox=crossprod(Coord(0,0,1),Oz) ;
52// choose Ox not too small to avoid rounding error
53  if (norm(Ox)< 0.1) Ox=crossprod(Coord(0,1,0),Oz) ;
54  Ox=Ox*(1./norm(Ox)) ;
55  Coord Oy=crossprod(Oz,Ox) ;
56  double cos_alpha;
57
58  /// vector<p2t::Point*> polyline;
59  for(int n=0; n<na;n++)
60  {
61    cos_alpha=scalarprod(OC,dstPolygon[n]) ;
62    a_gno[n].x=scalarprod(dstPolygon[n],Ox)/cos_alpha ;
63    a_gno[n].y=scalarprod(dstPolygon[n],Oy)/cos_alpha ;
64    a_gno[n].z=scalarprod(dstPolygon[n],Oz)/cos_alpha ; // must be equal to 1
65
66    vect_points.push_back( array<double, 2>() );
67    vect_points[n][0] = a_gno[n].x;
68    vect_points[n][1] = a_gno[n].y;
69
70  }
71
72  polyline.push_back(vect_points);
73  vector<N> indices_a_gno = mapbox::earcut<N>(polyline);
74 
75  double area_a_gno=0 ;
76  for(int i=0;i<indices_a_gno.size()/3;++i)
77    {
78      Coord x0 = Ox * polyline[0][indices_a_gno[3*i]][0] + Oy* polyline[0][indices_a_gno[3*i]][1] + Oz ;
79      Coord x1 = Ox * polyline[0][indices_a_gno[3*i+1]][0] + Oy* polyline[0][indices_a_gno[3*i+1]][1] + Oz ;
80      Coord x2 = Ox * polyline[0][indices_a_gno[3*i+2]][0] + Oy* polyline[0][indices_a_gno[3*i+2]][1] + Oz ;
81      area_a_gno+=triarea(x0 * (1./norm(x0)),x1* (1./norm(x1)), x2* (1./norm(x2))) ;
82    }
83
84  vect_points.clear();
85  polyline.clear();
86  indices_a_gno.clear();
87
88 
89
90  for(int n=0; n<nb;n++)
91  {
92    cos_alpha=scalarprod(OC,srcPolygon[n]) ;
93    b_gno[n].x=scalarprod(srcPolygon[n],Ox)/cos_alpha ;
94    b_gno[n].y=scalarprod(srcPolygon[n],Oy)/cos_alpha ;
95    b_gno[n].z=scalarprod(srcPolygon[n],Oz)/cos_alpha ; // must be equal to 1
96
97    vect_points.push_back( array<double, 2>() );
98    vect_points[n][0] = b_gno[n].x;
99    vect_points[n][1] = b_gno[n].y;
100  }
101
102
103  polyline.push_back(vect_points);
104  vector<N> indices_b_gno = mapbox::earcut<N>(polyline);
105
106  double area_b_gno=0 ;
107  for(int i=0;i<indices_b_gno.size()/3;++i)
108    {
109      Coord x0 = Ox * polyline[0][indices_b_gno[3*i]][0] + Oy* polyline[0][indices_b_gno[3*i]][1] + Oz ;
110      Coord x1 = Ox * polyline[0][indices_b_gno[3*i+1]][0] + Oy* polyline[0][indices_b_gno[3*i+1]][1] + Oz ;
111      Coord x2 = Ox * polyline[0][indices_b_gno[3*i+2]][0] + Oy* polyline[0][indices_b_gno[3*i+2]][1] + Oz ;
112      area_b_gno+=triarea(x0 * (1./norm(x0)),x1* (1./norm(x1)), x2* (1./norm(x2))) ;
113    }
114
115  vect_points.clear();
116  polyline.clear();
117  indices_b_gno.clear();
118
119
120// Compute intersections using clipper
121// 1) Compute offset and scale factor to rescale polygon
122
123  double xmin, xmax, ymin,ymax ;
124  xmin=xmax=a_gno[0].x ;
125  ymin=ymax=a_gno[0].y ;
126
127  for(int n=0; n<na;n++)
128  {
129    if (a_gno[n].x< xmin) xmin=a_gno[n].x ;
130    else if (a_gno[n].x > xmax) xmax=a_gno[n].x ;
131
132    if (a_gno[n].y< ymin) ymin=a_gno[n].y ;
133    else if (a_gno[n].y > ymax) ymax=a_gno[n].y ;
134  }
135
136  for(int n=0; n<nb;n++)
137  {
138    if (b_gno[n].x< xmin) xmin=b_gno[n].x ;
139    else if (b_gno[n].x > xmax) xmax=b_gno[n].x ;
140
141    if (b_gno[n].y< ymin) ymin=b_gno[n].y ;
142    else if (b_gno[n].y > ymax) ymax=b_gno[n].y ;
143  }
144
145  double xoffset=(xmin+xmax)*0.5 ;
146  double yoffset=(ymin+ymax)*0.5 ;
147  double xscale= 1e-4*0.5*hiRange/(xmax-xoffset) ;
148  double yscale= 1e-4*0.5*hiRange/(ymax-yoffset) ;
149// Problem with numerical precision if using larger scaling factor
150
151// 2) Compute intersection with clipper
152//    clipper use only long integer value for vertex => offset and rescale
153
154  Paths src(1), dst(1), intersection;
155
156  for(int n=0; n<na;n++)
157     src[0]<<IntPoint((a_gno[n].x-xoffset)*xscale,(a_gno[n].y-yoffset)*yscale) ;
158
159  for(int n=0; n<nb;n++)
160     dst[0]<<IntPoint((b_gno[n].x-xoffset)*xscale,(b_gno[n].y-yoffset)*yscale) ;
161
162  Clipper clip ;
163  clip.AddPaths(src, ptSubject, true);
164  clip.AddPaths(dst, ptClip, true);
165  clip.Execute(ctIntersection, intersection);
166 
167  double area=0 ;
168
169  for(int ni=0;ni<intersection.size(); ni++)
170  {
171    Coord* intersectPolygon=new Coord[intersection[ni].size()] ;
172    for(int n=0; n < intersection[ni].size(); n++)
173    {
174      intersectPolygon[n].x=intersection[ni][n].X/xscale+xoffset ;
175      intersectPolygon[n].y=intersection[ni][n].Y/yscale+yoffset ;
176    }
177   
178
179    int nv=0;
180
181    for(int n=0; n < intersection[ni].size(); n++)
182    {
183       double dx=intersectPolygon[n].x-intersectPolygon[(n+1)%intersection[ni].size()].x ;
184       double dy=intersectPolygon[n].y-intersectPolygon[(n+1)%intersection[ni].size()].y ;
185     
186       if (dx*dx+dy*dy>fusion_vertex*fusion_vertex)
187       {
188          intersectPolygon[nv]=intersectPolygon[n] ;
189
190          vect_points.push_back( array<double, 2>() );
191          vect_points[nv][0] = intersectPolygon[n].x;
192          vect_points[nv][1] = intersectPolygon[n].y;
193
194          nv++ ;
195       }
196     
197
198    }
199
200    polyline.push_back(vect_points);
201    vect_points.clear();
202
203    if (nv>2)
204    {
205 
206      vector<N> indices = mapbox::earcut<N>(polyline);
207
208      double area2=0 ;
209      for(int i=0;i<indices.size()/3;++i)
210      {
211          Coord x0 = Ox * polyline[0][indices[3*i]][0] + Oy* polyline[0][indices[3*i]][1] + Oz ;
212          Coord x1 = Ox * polyline[0][indices[3*i+1]][0] + Oy* polyline[0][indices[3*i+1]][1] + Oz ;
213          Coord x2 = Ox * polyline[0][indices[3*i+2]][0] + Oy* polyline[0][indices[3*i+2]][1] + Oz ;
214          area2+=triarea(x0 * (1./norm(x0)),x1* (1./norm(x1)), x2* (1./norm(x2))) ;
215      }
216
217      polyline.clear();
218
219      for(int n=0; n < nv; n++)
220      {
221        intersectPolygon[n] = Ox*intersectPolygon[n].x+Oy*intersectPolygon[n].y+Oz;
222      }
223
224
225//     assign intersection to source and destination polygons
226       Polyg *is = new Polyg;
227       is->x = exact_barycentre(intersectPolygon,nv);
228//       is->area = polygonarea(intersectPolygon,nv) ;
229       is->area = area2 ;
230
231//        if (is->area < 1e-12) cout<<"Small intersection : "<<is->area<<endl ;
232       if (is->area==0.) delete is ;
233       else
234       { 
235         is->id = b->id; /* intersection holds id of corresponding source element (see Elt class definition for details about id) */
236         is->src_id = b->src_id;
237         is->n = nv;
238         (a->is).push_back(is);
239         (b->is).push_back(is);
240         area=is->area ;
241       }
242    }
243    delete[] intersectPolygon ;
244  }
245
246  delete[] a_gno ;
247  delete[] b_gno ;
248  return area ;
249
250}
251
252
253
254void createGreatCirclePolygon(const Elt& element, const Coord& pole, vector<Coord>& coordinates)
255{
256  int nv = element.n;
257
258  double z,r ;
259  int north ;
260  int iterations ;
261
262  Coord xa,xb,xi,xc ;
263  Coord x1,x2,x ;
264
265  for(int i=0;i < nv ;i++)
266  {
267    north = (scalarprod(element.edge[i], pole) < 0) ? -1 : 1;
268    z=north*element.d[i] ;
269
270    if (z != 0.0)
271    {
272
273      xa=element.vertex[i] ;
274      xb=element.vertex[(i+1)%nv] ;
275      iterations=0 ;
276
277// compare max distance (at mid-point) between small circle and great circle
278// if greater the epsilon refine the small circle by dividing it recursively.
279
280      do
281      {
282        xc = pole * z ;
283        r=sqrt(1-z*z) ;
284        xi=(xa+xb)*0.5 ;
285        x1=xc+(xi-xc)*(r/norm(xi-xc)) ;
286        x2= xi*(1./norm(xi)) ;
287        ++iterations;
288        xb=x1 ;
289      } while(norm(x1-x2)/norm(xa-xb)>epsilon) ;
290
291      iterations = 1 << (iterations-1) ;
292
293// small circle divided in "iterations" great circle arc
294      Coord delta=(element.vertex[(i+1)%nv]-element.vertex[i])*(1./iterations);
295      x=xa ;
296      for(int j=0; j<iterations ; j++)
297      {
298        //xc+(x-xc)*r/norm(x-xc)
299        coordinates.push_back(xc+(x-xc)*(r/norm(x-xc))) ;
300        x=x+delta ;
301      }
302    }
303    else coordinates.push_back(element.vertex[i]) ;
304  }
305}
306
307}
Note: See TracBrowser for help on using the repository browser.