; docformat = 'rst rst'
;
;+
; :Author: Paulo Penteado (http://www.ppenteado.net), Nov/2014
;-
;+
; :Description:
; Given a set of lon/lat points on a sphere, this function calculates vertices of
; a path connecting the input vertices, placed along great circles connecting the
; input vertices. The path is a polygon approximation of the curved path formed by
; the great circles.
;
; :Returns:
; A 2D array [2,N] with a set of longitudes/latitudes for the N vertices of the path, for each path.
; The number of vertices along each edge is determined by `maxlength`
; or `nsegments`. If `open` is set, the last point is not connected to the first point.
; If multiple polygons were given as input, the ouput is a list, where each list element is a [2,N] array
; with lon/lat for each polygon (N needs not to be the same for all polygons).
;
; :Params:
; lons: in, required
; An array of longitudes for the vertices which are to be connected by a path made of great circles.
; Must be in degrees, unless `radians` is set.
; Multiple polygons are supported in two different ways: 1) If all N polygons have the same number of
; vertices (M), lons can be given as a [M,N] array. 2) For arbitrary numbers of vertices, lons is given
; as a list, where each list element is an array of vertices for one polygon. With multiple polygons, the
; ouput is a list, where each list element has latitudes and longitudes for one polygon.
; lats: in, required
; An array of latitudes for the vertices which are to be connected by a path made of great circles.
; Must be in degrees, unless `radians` is set.
; Multiple polygons are supported in two different ways: 1) If all N polygons have the same number of
; vertices (M), lats can be given as a [M,N] array. 2) For arbitrary numbers of vertices, lats is given
; as a list, where each list element is an array of vertices for one polygon. With multiple polygons, the
; ouput is a list, where each list element has latitudes and longitudes for one polygon.
;
; :Keywords:
; maxlength: in, optional, default=0.1
; If set, specifies the maximum angular length of each segment of the polygon.
; nsegments: in, optional
; If set, specifies a fixed number of vertices to use in for the line connecting each pair
; of input points. If both `maxlength` and `nsegments` are set, `nsegments` takes precendence.
; open: in, optional, default=0
; If set, the output path does not connect the last input point to the first input point.
; _ref_extra: in, out, optional
; Anything else is passed along to map_2points, which is used to create the paths connecting the
; pairs of input points. One commonly used option would be the radians keyword, to use radians,
; instead of degrees, for `lon` and `lat`. See the IDL help for map_2points' options.
;
; :Examples:
; Create a rectangle in lon/lat::
; lons=[30d0,120d0,120d0,30d0]
; lats=[70d0,70d0,10d0,10d0]
; Create a closed path connecting these points::
; path=pp_sphericalpath(lons,lats)
; Create a base map to plot the results (from IDL's imap example)::
; file = FILEPATH('avhrr.png', SUBDIRECTORY=['examples','data'])
; data = READ_PNG(file, r, g, b)
; IMAP, data, LIMIT=[-90,-180,90,180], $
; MAP_PROJECTION='Mollweide', RGB_TABLE=[[r],[g],[b]], $
; IMAGE_TRANSPARENCY=50, GRID_UNITS=2, $
; IMAGE_LOCATION=[-180,-90], IMAGE_DIMENSIONS=[360,180]
; Plot the input points connected by straight lines::
; iplot,lons[[0,1,2,3,0]],lats[[0,1,2,3,0]],color='red',/overplot
; Plot the path created by pp_sphericalpath::
; iplot,/overplot,color='green',path,thick=3.
; The result should look like
; .. image:: pp_sphericalpath_example_1.png
;
; The green path, produced with the output of `pp_sphericalpath` looks
; like a curve, made by the 4 great circles connecting these points. In reality,
; the path is made of 2367 points, separated by 0.1 degrees.
; This can be verified by making the same plot on a gnomonic projection, which has
; the property that great circles are straight lines::
; imap,data,limit=[0,20,80,130],map_projection='mollweide',rgb_table=[[r],[g],[b]],$
; grid_units=2,image_transparency=50,image_location=[-180,-90],$
; image_dimensions=[360,180],center_latitude=50,center_longitude=75
; iplot,/overplot,color='green',path,thick=3.
;
; The result should look like
; .. image:: pp_sphericalpath_example_2.png
;
; Where the green path is rendered as straight lines, since great circles map
; into straight lines in a gnomonic projection.
;
;
; :Author: Paulo Penteado (http://www.ppenteado.net), Nov/2014
;-
function pp_sphericalpath,lons,lats,maxlength=maxlength,nsegments=nsegments,$
open=open,_ref_extra=ex,no_fix_lon=no_fix_lon
compile_opt idl2,logical_predicate
;Defaults
no_fix_lon=keyword_set(no_fix_lon)
open=keyword_set(open)
slats=size(lats,/dimensions)
slons=size(lons,/dimensions)
if ~array_equal(slats,slons) then message,'lons must have same dimensions as lats'
listres=1B
if isa(lons,'list') && isa (lats,'list') then begin
npolys=n_elements(lons)
endif else begin
npo=slats[0]
npolys=n_elements(lats)/npo
if npolys eq 1 then listres=0B
endelse
lres=list()
for ipoly=0,npolys-1 do begin
if isa(lons,'list') && isa (lats,'list') then begin
olons=lons[ipoly] & olats=lats[ipoly]
endif else begin
olons=lons[ipoly*npo:(ipoly+1)*npo-1]
olats=lats[ipoly*npo:(ipoly+1)*npo-1]
endelse
npoints=n_elements(olons)
;Check arguments
if npoints lt 2 then message,'lons must have at least two elements'
if n_elements(olats) ne npoints then message,'lats must have the same number of elements as lons'
;Make set of vertices
np=open ? npoints : npoints+1
ilons=open ? olons : [olons,olons[0]]
ilats=open ? olats : [olats,olats[0]]
;Create output
ret=ptrarr(np-1)
count=lon64arr(np)
maxlength=n_elements(maxlength) ? maxlength : 0.1d0
for i=0LL,np-2LL do begin
if n_elements(nsegments) then begin
if nsegments ge 1 then tmp=map_2points(ilons[i],ilats[i],ilons[i+1],ilats[i+1],npath=nsegments+1,_strict_extra=ex) else begin
tmp=map_2points(ilons[i],ilats[i],ilons[i+1],ilats[i+1],npath=2,_strict_extra=ex)
countt=n_elements(tmp)/2LL
tmp=i eq 0 ? tmp[*,[0,countt-1]] : tmp[*,[countt-1]]
endelse
endif else begin
tmp=replicate(!values.d_nan,[2,3])
if total(finite([ilons[i],ilats[i],ilons[i+1],ilats[i+1]])) eq 4 then $
tmp=map_2points(ilons[i],ilats[i],ilons[i+1],ilats[i+1],dpath=maxlength,_strict_extra=ex)
endelse
if ~no_fix_lon then begin
tmp[0,*]=(tmp[0,*]+360d0) mod 360
w=where(tmp[0,*] gt 180d0,wc)
if wc then tmp[0,w]-=360d0
endif
count[i+1]=n_elements(tmp)/2LL
ret[i]=ptr_new(tmp)
endfor
res=dblarr(2,total(count,/integer))
count=total(count,/cumulative,/integer)
for itmp=0LL,np-2LL do begin
res[count[itmp]*2LL]=(*(ret[itmp]))[*]
endfor
if listres then lres.add,res
endfor
if listres then res=lres
return,res
end