@@ -7976,7 +7976,7 @@ uint64_t gmtlib_lonpath (struct GMT_CTRL *GMT, double lon, double lat1, double l
79767976 uint64_t n , k ;
79777977 int n_try , pos ;
79787978 bool keep_trying ;
7979- double dlat , dlat0 , * tlon = NULL , * tlat = NULL , x0 , x1 , y0 , y1 , d , min_gap ;
7979+ double dlat , dlat0 , * tlon = NULL , * tlat = NULL , x0 , x1 , y0 , y1 , d , min_gap , final_d , x_prev , y_prev ;
79807980
79817981 if (GMT -> current .map .meridian_straight == 2 ) { /* Special non-sampling for gmtselect/grdlandmask */
79827982 gmt_M_malloc2 (GMT , tlon , tlat , 2U , NULL , double );
@@ -8041,10 +8041,26 @@ uint64_t gmtlib_lonpath (struct GMT_CTRL *GMT, double lon, double lat1, double l
80418041 keep_trying = false;
80428042 }
80438043 } while (keep_trying && n_try < 10 );
8044+ /* See gmtlib_lonpath above (#8684). Limit to azimuthal full-sphere case to avoid
8045+ * affecting projections where large chords are legitimate. */
8046+ if (gmt_M_is_azimuthal (GMT ) && GMT -> current .proj .f_horizon >= 180.0 - GMT_CONV4_LIMIT ) {
8047+ final_d = hypot (x1 - x0 , y1 - y0 );
8048+ if (final_d > 5.0 * GMT -> current .setting .map_line_step || gmt_M_is_dnan (x1 ) || gmt_M_is_dnan (y1 ))
8049+ tlat [n ] = GMT -> session .d_NaN ;
8050+ }
80448051 x0 = x1 ; y0 = y1 ;
80458052 }
80468053 tlon [n ] = lon ;
80478054 tlat [n ] = lat2 ;
8055+ /* Check if forced endpoint at lat2 makes a wild jump from previous sample (e.g. antipode of the
8056+ * azimuthal pole). If so, flag NaN so gmt_geo_to_xy_line breaks the polyline (#8684). */
8057+ if (n > 0 && !gmt_M_is_dnan (tlat [n - 1 ]) && gmt_M_is_azimuthal (GMT ) && GMT -> current .proj .f_horizon >= 180.0 - GMT_CONV4_LIMIT ) {
8058+ gmt_geo_to_xy (GMT , tlon [n - 1 ], tlat [n - 1 ], & x_prev , & y_prev );
8059+ gmt_geo_to_xy (GMT , tlon [n ], tlat [n ], & x1 , & y1 );
8060+ if (gmt_M_is_dnan (x1 ) || gmt_M_is_dnan (y1 ) || gmt_M_is_dnan (x_prev ) || gmt_M_is_dnan (y_prev ) ||
8061+ hypot (x1 - x_prev , y1 - y_prev ) > 5.0 * GMT -> current .setting .map_line_step )
8062+ tlat [n ] = GMT -> session .d_NaN ;
8063+ }
80488064 n ++ ;
80498065
80508066 if (n != n_alloc ) {
@@ -8062,7 +8078,7 @@ uint64_t gmtlib_latpath (struct GMT_CTRL *GMT, double lat, double lon1, double l
80628078 uint64_t k , n ;
80638079 int n_try , pos ;
80648080 bool keep_trying ;
8065- double dlon , dlon0 , * tlon = NULL , * tlat = NULL , x0 , x1 , y0 , y1 , d , min_gap ;
8081+ double dlon , dlon0 , * tlon = NULL , * tlat = NULL , x0 , x1 , y0 , y1 , d , min_gap , final_d , x_prev , y_prev ;
80668082
80678083 if (GMT -> current .map .parallel_straight == 2 ) { /* Special non-sampling for gmtselect/grdlandmask */
80688084 gmt_M_malloc2 (GMT , tlon , tlat , 2U , NULL , double );
@@ -8116,10 +8132,25 @@ uint64_t gmtlib_latpath (struct GMT_CTRL *GMT, double lat, double lon1, double l
81168132 keep_trying = false;
81178133 }
81188134 } while (keep_trying && n_try < 10 );
8135+ /* If the final projected step is far larger than the desired map_line_step, the parallel
8136+ * crossed a projection singularity (antipode of azimuthal full-sphere pole). Mark with NaN
8137+ * so gmt_geo_to_xy_line breaks the polyline (#8684). */
8138+ if (gmt_M_is_azimuthal (GMT ) && GMT -> current .proj .f_horizon >= 180.0 - GMT_CONV4_LIMIT ) {
8139+ final_d = hypot (x1 - x0 , y1 - y0 );
8140+ if (final_d > 5.0 * GMT -> current .setting .map_line_step || gmt_M_is_dnan (x1 ) || gmt_M_is_dnan (y1 ))
8141+ tlat [n ] = GMT -> session .d_NaN ;
8142+ }
81198143 x0 = x1 ; y0 = y1 ;
81208144 }
81218145 tlon [n ] = lon2 ;
81228146 tlat [n ] = lat ;
8147+ if (n > 0 && !gmt_M_is_dnan (tlat [n - 1 ]) && gmt_M_is_azimuthal (GMT ) && GMT -> current .proj .f_horizon >= 180.0 - GMT_CONV4_LIMIT ) { /* Detect wild endpoint jump (#8684) */
8148+ gmt_geo_to_xy (GMT , tlon [n - 1 ], tlat [n - 1 ], & x_prev , & y_prev );
8149+ gmt_geo_to_xy (GMT , tlon [n ], tlat [n ], & x1 , & y1 );
8150+ if (gmt_M_is_dnan (x1 ) || gmt_M_is_dnan (y1 ) || gmt_M_is_dnan (x_prev ) || gmt_M_is_dnan (y_prev ) ||
8151+ hypot (x1 - x_prev , y1 - y_prev ) > 5.0 * GMT -> current .setting .map_line_step )
8152+ tlat [n ] = GMT -> session .d_NaN ;
8153+ }
81238154 n ++ ;
81248155 n_alloc = n ;
81258156 gmt_M_malloc2 (GMT , tlon , tlat , 0 , & n_alloc , double );
@@ -8221,7 +8252,8 @@ uint64_t gmt_geo_to_xy_line(struct GMT_CTRL *GMT, double *lon, double *lat, uint
82218252 }
82228253
82238254 if ((nx = gmtmap_crossing (GMT , lon [j - 1 ], lat [j - 1 ], lon [j ], lat [j ], xlon , xlat , xx , yy , sides ))) { /* Do nothing if we get crossings*/ }
8224- else if (GMT -> current .map .is_world ) /* Check global wrapping if 360 range */
8255+ else if (GMT -> current .map .is_world && /* Check global wrapping if 360 range */
8256+ !(gmt_M_is_azimuthal (GMT ) && !gmt_M_is_perspective (GMT ))) /* Azimuthal projections (except ortho/genper) have a closed boundary; cylindrical wrap check makes spurious chord crossings (#8684) */
82258257 nx = (* GMT -> current .map .wrap_around_check ) (GMT , dummy , last_x , last_y , this_x , this_y , xx , yy , sides );
82268258
82278259 if (nx == 1 ) { /* inside-outside or outside-inside; set move&clip vs draw flags */
0 commit comments