@@ -55,9 +55,8 @@ function calcGeoJSON(calcTrace, topojson) {
5555 var trace = calcTrace [ 0 ] . trace ;
5656 var len = calcTrace . length ;
5757 var features = getTopojsonFeatures ( trace , topojson ) ;
58- var i , j , k , m ;
5958
60- for ( i = 0 ; i < len ; i ++ ) {
59+ for ( var i = 0 ; i < len ; i ++ ) {
6160 var calcPt = calcTrace [ i ] ;
6261 var feature = locationToFeature ( trace . locationmode , calcPt . loc , features ) ;
6362
@@ -66,48 +65,116 @@ function calcGeoJSON(calcTrace, topojson) {
6665 continue ;
6766 }
6867
68+
6969 calcPt . geojson = feature ;
7070 calcPt . ct = feature . properties . ct ;
7171 calcPt . index = i ;
72+ calcPt . _polygons = feature2polygons ( feature ) ;
73+ }
74+ }
7275
73- var geometry = feature . geometry ;
74- var coords = geometry . coordinates ;
75- calcPt . _polygons = [ ] ;
76+ function feature2polygons ( feature ) {
77+ var geometry = feature . geometry ;
78+ var coords = geometry . coordinates ;
79+ var loc = feature . id ;
80+
81+ var polygons = [ ] ;
82+ var appendPolygon , j , k , m ;
83+
84+ function doesCrossAntiMerdian ( pts ) {
85+ for ( var l = 0 ; l < pts . length - 1 ; l ++ ) {
86+ if ( pts [ l ] [ 0 ] > 0 && pts [ l + 1 ] [ 0 ] < 0 ) return l ;
87+ }
88+ return null ;
89+ }
7690
91+ if ( loc === 'RUS' || loc === 'FJI' ) {
7792 // Russia and Fiji have landmasses that cross the antimeridian,
7893 // we need to add +360 to their longitude coordinates, so that
7994 // polygon 'contains' doesn't get confused when crossing the antimeridian.
8095 //
8196 // Note that other countries have polygons on either side of the antimeridian
8297 // (e.g. some Aleutian island for the USA), but those don't confuse
8398 // the 'contains' method; these are skipped here.
84- if ( calcPt . loc === 'RUS' || calcPt . loc === 'FJI' ) {
85- for ( j = 0 ; j < coords . length ; j ++ ) {
86- for ( k = 0 ; k < coords [ j ] . length ; k ++ ) {
87- for ( m = 0 ; m < coords [ j ] [ k ] . length ; m ++ ) {
88- if ( coords [ j ] [ k ] [ m ] [ 0 ] < 0 ) {
89- coords [ j ] [ k ] [ m ] [ 0 ] += 360 ;
90- }
91- }
99+ appendPolygon = function ( _pts ) {
100+ var pts ;
101+
102+ if ( doesCrossAntiMerdian ( _pts ) === null ) {
103+ pts = _pts ;
104+ } else {
105+ pts = new Array ( _pts . length ) ;
106+ for ( m = 0 ; m < _pts . length ; m ++ ) {
107+ // do nut mutate calcdata[i][j].geojson !!
108+ pts [ m ] = [
109+ _pts [ m ] [ 0 ] < 0 ? _pts [ m ] [ 0 ] + 360 : _pts [ m ] [ 0 ] ,
110+ _pts [ m ] [ 1 ]
111+ ] ;
92112 }
93113 }
94- }
95114
96- switch ( geometry . type ) {
97- case 'MultiPolygon' :
98- for ( j = 0 ; j < coords . length ; j ++ ) {
99- for ( k = 0 ; k < coords [ j ] . length ; k ++ ) {
100- calcPt . _polygons . push ( polygon . tester ( coords [ j ] [ k ] ) ) ;
101- }
115+ polygons . push ( polygon . tester ( pts ) ) ;
116+ } ;
117+ } else if ( loc === 'ATA' ) {
118+ // Antarctica has a landmass that wraps around every longitudes which
119+ // confuses the 'contains' methods.
120+ appendPolygon = function ( pts ) {
121+ var crossAntiMeridianIndex = doesCrossAntiMerdian ( pts ) ;
122+
123+ // polygon that do not cross anti-meridian need no special handling
124+ if ( crossAntiMeridianIndex === null ) {
125+ return polygons . push ( polygon . tester ( pts ) ) ;
126+ }
127+
128+ // stitch polygon by adding pt over South Pole,
129+ // so that it covers the projected region covers all latitudes
130+ //
131+ // Note that the algorithm below only works for polygons that
132+ // start and end on longitude -180 (like the ones built by
133+ // https://github.com/etpinard/sane-topojson).
134+ var stitch = new Array ( pts . length + 1 ) ;
135+ var si = 0 ;
136+
137+ for ( m = 0 ; m < pts . length ; m ++ ) {
138+ if ( m > crossAntiMeridianIndex ) {
139+ stitch [ si ++ ] = [ pts [ m ] [ 0 ] + 360 , pts [ m ] [ 1 ] ] ;
140+ } else if ( m === crossAntiMeridianIndex ) {
141+ stitch [ si ++ ] = pts [ m ] ;
142+ stitch [ si ++ ] = [ pts [ m ] [ 0 ] , - 90 ] ;
143+ } else {
144+ stitch [ si ++ ] = pts [ m ] ;
102145 }
103- break ;
104- case 'Polygon' :
105- for ( j = 0 ; j < coords . length ; j ++ ) {
106- calcPt . _polygons . push ( polygon . tester ( coords [ j ] ) ) ;
146+ }
147+
148+ // polygon.tester by default appends pt[0] to the points list,
149+ // we must remove it here, to avoid a jump in longitude from 180 to -180,
150+ // that would confuse the 'contains' method
151+ var tester = polygon . tester ( stitch ) ;
152+ tester . pts . pop ( ) ;
153+ polygons . push ( tester ) ;
154+ } ;
155+ } else {
156+ // otherwise using same array ref is fine
157+ appendPolygon = function ( pts ) {
158+ polygons . push ( polygon . tester ( pts ) ) ;
159+ } ;
160+ }
161+
162+ switch ( geometry . type ) {
163+ case 'MultiPolygon' :
164+ for ( j = 0 ; j < coords . length ; j ++ ) {
165+ for ( k = 0 ; k < coords [ j ] . length ; k ++ ) {
166+ appendPolygon ( coords [ j ] [ k ] ) ;
107167 }
108- break ;
109- }
168+ }
169+ break ;
170+ case 'Polygon' :
171+ for ( j = 0 ; j < coords . length ; j ++ ) {
172+ appendPolygon ( coords [ j ] ) ;
173+ }
174+ break ;
110175 }
176+
177+ return polygons ;
111178}
112179
113180function style ( geo ) {
0 commit comments