@@ -253,7 +253,8 @@ d3.geo.mercator = function() {
253253d3 . geo . path = function ( ) {
254254 var pointRadius = 4.5 ,
255255 pointCircle = d3_path_circle ( pointRadius ) ,
256- projection = d3 . geo . albersUsa ( ) ;
256+ projection = d3 . geo . albersUsa ( ) ,
257+ clip = Object ;
257258
258259 function path ( d , i ) {
259260 if ( typeof pointRadius === "function" ) {
@@ -282,12 +283,14 @@ d3.geo.path = function() {
282283 } ,
283284
284285 Point : function ( o ) {
285- return "M" + project ( o . coordinates ) + pointCircle ;
286+ var coordinates = clip . call ( this , [ o . coordinates ] ) ;
287+ return coordinates . length
288+ ? "M" + project ( coordinates [ 0 ] ) + pointCircle : "" ;
286289 } ,
287290
288291 MultiPoint : function ( o ) {
289292 var path = [ ] ,
290- coordinates = o . coordinates ,
293+ coordinates = clip . call ( this , o . coordinates ) ,
291294 i = - 1 , // coordinates.index
292295 n = coordinates . length ;
293296 while ( ++ i < n ) path . push ( "M" , project ( coordinates [ i ] ) , pointCircle ) ;
@@ -296,7 +299,7 @@ d3.geo.path = function() {
296299
297300 LineString : function ( o ) {
298301 var path = [ "M" ] ,
299- coordinates = o . coordinates ,
302+ coordinates = clip . call ( this , o . coordinates ) ,
300303 i = - 1 , // coordinates.index
301304 n = coordinates . length ;
302305 while ( ++ i < n ) path . push ( project ( coordinates [ i ] ) , "L" ) ;
@@ -313,7 +316,7 @@ d3.geo.path = function() {
313316 j , // subcoordinates.index
314317 m ; // subcoordinates.length
315318 while ( ++ i < n ) {
316- subcoordinates = coordinates [ i ] ;
319+ subcoordinates = clip . call ( this , coordinates [ i ] ) ;
317320 j = - 1 ;
318321 m = subcoordinates . length ;
319322 path . push ( "M" ) ;
@@ -332,9 +335,10 @@ d3.geo.path = function() {
332335 j , // subcoordinates.index
333336 m ; // subcoordinates.length
334337 while ( ++ i < n ) {
335- subcoordinates = coordinates [ i ] ;
338+ subcoordinates = clip . call ( this , coordinates [ i ] ) ;
336339 j = - 1 ;
337- m = subcoordinates . length ;
340+ m = subcoordinates . length - 1 ;
341+ if ( m < 1 ) continue ;
338342 path . push ( "M" ) ;
339343 while ( ++ j < m ) path . push ( project ( subcoordinates [ j ] ) , "L" ) ;
340344 path [ path . length - 1 ] = "Z" ;
@@ -358,9 +362,10 @@ d3.geo.path = function() {
358362 j = - 1 ;
359363 m = subcoordinates . length ;
360364 while ( ++ j < m ) {
361- subsubcoordinates = subcoordinates [ j ] ;
365+ subsubcoordinates = clip . call ( this , subcoordinates [ j ] ) ;
362366 k = - 1 ;
363367 p = subsubcoordinates . length - 1 ;
368+ if ( p < 1 ) continue ;
364369 path . push ( "M" ) ;
365370 while ( ++ k < p ) path . push ( project ( subsubcoordinates [ k ] ) , "L" ) ;
366371 path [ path . length - 1 ] = "Z" ;
@@ -498,6 +503,12 @@ d3.geo.path = function() {
498503 return path ;
499504 } ;
500505
506+ path . clip = function ( x ) {
507+ if ( ! arguments . length ) return clip ;
508+ clip = x ;
509+ return path ;
510+ }
511+
501512 path . area = function ( d ) {
502513 return d3_geo_pathType ( areaTypes , d ) ;
503514 } ;
@@ -616,7 +627,7 @@ d3.geo.greatCircle = function() {
616627 radius = 6371 ; // Mean radius of Earth, in km.
617628 // TODO: breakAtDateLine?
618629
619- function greatCircle ( d , i ) {
630+ function greatCircle ( d , i , dist ) {
620631 var from = source . call ( this , d , i ) ,
621632 to = target . call ( this , d , i ) ,
622633 x0 = from [ 0 ] * d3_radians ,
@@ -627,13 +638,15 @@ d3.geo.greatCircle = function() {
627638 cy0 = Math . cos ( y0 ) , sy0 = Math . sin ( y0 ) ,
628639 cx1 = Math . cos ( x1 ) , sx1 = Math . sin ( x1 ) ,
629640 cy1 = Math . cos ( y1 ) , sy1 = Math . sin ( y1 ) ,
630- d = Math . acos ( sy0 * sy1 + cy0 * cy1 * Math . cos ( x1 - x0 ) ) ,
641+ d = dist || Math . acos ( sy0 * sy1 + cy0 * cy1 * Math . cos ( x1 - x0 ) ) ,
631642 sd = Math . sin ( d ) ,
632643 f = d / ( n - 1 ) ,
633644 e = - f ,
634645 path = [ ] ,
635646 i = - 1 ;
636647
648+ if ( d === 0 ) return [ from ] ;
649+
637650 while ( ++ i < n ) {
638651 e += f ;
639652 var A = Math . sin ( d - e ) / sd ,
@@ -699,4 +712,89 @@ function d3_geo_greatCircleSource(d) {
699712function d3_geo_greatCircleTarget ( d ) {
700713 return d . target ;
701714}
715+ d3 . geo . clip = function ( ) {
716+ var origin = [ 0 , 0 ] ,
717+ angle = 90 ,
718+ r = 6371 * angle / 180 * Math . PI ;
719+
720+ function clip ( d ) {
721+ var o = { source : origin , target : null } ,
722+ n = d . length ,
723+ i = - 1 ,
724+ j ,
725+ path ,
726+ clipped = [ ] ,
727+ p = null ,
728+ q = null ;
729+ while ( ++ i < n ) {
730+ o . target = d [ i ] ;
731+ distance = d3_geo_clipGreatCircle . distance ( o ) ;
732+ if ( distance < r ) {
733+ if ( q ) {
734+ path = d3_geo_clipGreatCircle ( { source : q , target : o . target } ) ;
735+ j = d3_geo_clipClosest ( path , o , r ) ;
736+ if ( p ) {
737+ clipped . push . apply ( clipped , d3_geo_clipGreatCircle ( { source : p , target : o . target } ) ) ;
738+ }
739+ clipped . push . apply ( clipped , path . slice ( j ) ) ;
740+ p = q = null ;
741+ } else {
742+ clipped . push ( o . target ) ;
743+ }
744+ } else {
745+ q = o . target ;
746+ if ( ! p && clipped . length ) {
747+ path = d3_geo_clipGreatCircle ( { source : clipped [ clipped . length - 1 ] , target : o . target } ) ;
748+ j = d3_geo_clipClosest ( path , o , r ) ;
749+ clipped . push . apply ( clipped , path . slice ( 0 , j ) ) ;
750+ p = o . target ;
751+ }
752+ }
753+ }
754+ if ( q && clipped . length ) {
755+ o . target = clipped [ 0 ] ;
756+ path = d3_geo_clipGreatCircle ( { source : q , target : o . target } ) ;
757+ j = d3_geo_clipClosest ( path , o , r ) ;
758+ if ( p ) {
759+ clipped . push . apply ( clipped , d3_geo_clipGreatCircle ( { source : p , target : o . target } ) ) ;
760+ }
761+ clipped . push . apply ( clipped , path . slice ( j ) ) ;
762+ }
763+ return clipped ;
764+ }
765+
766+ clip . origin = function ( x ) {
767+ if ( ! arguments . length ) return origin ;
768+ origin = x ;
769+ return clip ;
770+ } ;
771+
772+ clip . angle = function ( x ) {
773+ if ( ! arguments . length ) return angle ;
774+ angle = + x ;
775+ r = 6371 * angle / 180 * Math . PI ;
776+ return clip ;
777+ } ;
778+
779+ return clip ;
780+ }
781+
782+ var d3_geo_clipGreatCircle = d3 . geo . greatCircle ( ) . n ( 100 ) ;
783+
784+ function d3_geo_clipClosest ( path , o , r ) {
785+ var i = - 1 ,
786+ n = path . length ,
787+ index = 0 ,
788+ best = Infinity ;
789+ while ( ++ i < n ) {
790+ o . target = path [ i ] ;
791+ var d = Math . abs ( d3_geo_clipGreatCircle . distance ( o ) - r ) ;
792+ if ( d < best ) {
793+ best = d ;
794+ index = i ;
795+ }
796+ }
797+ o . target = path [ index ] ;
798+ return index ;
799+ }
702800} ) ( ) ;
0 commit comments