X
4722

Plotting the globe 360 degrees from an arbitrary starting point

One of the really nice things about IDL's function graphics is that you have the ability to go crazy customizing plots and maps to make some cool visualizations. This help article is meant to be a longer example example for how this can be done with two maps. For this case, we are plotting the globe a full 360 degrees from an arbitrary starting point. This can be useful because the standard map in IDL is limited to +/- 180 degrees and data spaces get strange if you try to go beyond those limits.

To get around this, one map is split up into two separate maps. The first map goes from the starting longitude to 180 degrees West, while the second map goes from 180 degrees East to the starting longitude. In addition to just making maps, the code below is set up to also overplot data into these data spaces. If you want to plot data, you must use the keywords XDATA and YDATA for the longitude and latitude data points. If you are plotting all the way around the globe, you must have XDATA extend 360 degrees past the starting point. In other words, if we start at -90 degrees the final longitude should be 270 degrees.

To recreate the map above, save the code below as as 'full_world_map.pro' and type the following line into the IDL console: 


projection = 'equirectangular'
lonstart = -62.
npoints = 360

;make some data to overplot
x = findgen(npoints, START=lonstart, increment = 360./(npoints+1))
y = 60*sin(x*(2*!PI/90.))

full_world_map, projection, lonstart, XDATA = x, YDATA = y


The number -62 corresponds to the starting longitude where the globe will be plotted 360 degrees clockwise from.The starting longitude should fall between -180 and 180 degrees. The different projection names that have been tested are: 'geographic', 'mercator', 'equirectangular''cylindrical', 'cylindrical equal area', and 'miller cylindrical'.
Here is the program code:

 

pro full_world_map, projection, lonstart, XDATA = xdata, YDATA = ydata
    compile_opt idl2
    ireset, /no_prompt

    plot_color = 'red'
    plot_thickness = 1.0
    latrange = [-90.,90.]
    
    ;specify the degrees between each label on the map
    lonlabelspacing = 45
    latlabelspacing = 30
    
    ;specify the longitude range for the first map
    lonrange_map1 = [lonstart, 180.]
    ;specify the longitude range for the second map
    lonrange_map2 = [-180., lonstart]
    
    ;check against a list of maps that need 0 for center longitude
    badmaps = ['geographic']
    if (total(projection eq badmaps) ge 1) then begin
        center_longitude1 = 0
        center_longitude2 = 0
    endif else begin
        center_longitude1 = .5*total(lonrange_map1)
        center_longitude2 = .5*total(lonrange_map2)
    endelse
    
    ;find the correct positions for the maps within the window
    maprange = [.1,.9]
    ratio_of_maprange = (lonrange_map1[1]-lonrange_map1[0])/360.
    pos_map1 = [maprange[0],maprange[0],$ ; X1, Y1
        maprange[0] + ratio_of_maprange*(maprange[1] - maprange[0]),maprange[1]]; X2, Y2
    pos_map2=[(ratio_of_maprange)*(maprange[1]-maprange[0])+maprange[0] ,maprange[0],$; X1,Y1
        maprange[1], maprange[1]] ; X2, Y2

    ;create our plot window
    w1 = window(window_title = 'Super Neat Globe Example',dimensions = [1300,800])
    w1.refresh, /disable
    
    ;draw the first portion of the map
    Map1 = MAP( projection, FILL_COLOR='Light Blue', COLOR='Gray', $
        LIMIT= [latrange[0], lonrange_map1[0], latrange[1], lonrange_map1[1] ], $
        LINESTYLE='Dotted', LABEL_POSITION=0, position = pos_map1,$
        center_longitude = center_longitude1, /current,$
        GRID_LONGITUDE = lonlabelspacing, GRID_LATITUDE = latlabelspacing)
    Map1.position = pos_map1 ;need to set this again for it to be set sometimes
    ;plot the continents
    mc = MAPCONTINENTS(/continents, FILL_COLOR='beige',/noclip)

    ;if we have data specified to overplot
    if keyword_set(xdata) AND keyword_set(xdata) then begin
        ;if we only have one map, then only one call to plot is needed
        if (floor(lonrange_map1[1] - lonrange_map1[0]) eq 360) then begin
            p1 = plot(xdata, ydata, current = map1, /overplot, $
                color = plot_color, THICK = plot_thickness)
        endif else begin
            ;separate data that goes to map 1 and map2
            map1_indices = where((xdata gt lonstart) AND (xdata lt 180.))
            p1 = plot(xdata[map1_indices], ydata[map1_indices], current = map1, $
                /overplot, clip = 0, color = plot_color, thick = plot_thickness)
        endelse
    endif
    
    
    ;draw the other part of the globe if the first plot does not extend 360 degrees
    if (floor(lonrange_map1[1] - lonrange_map1[0]) ne 360) then begin
        map2 = MAP( projection, FILL_COLOR='Light Blue', COLOR='Gray', $
            LIMIT=[ latrange[0], lonrange_map2[0], latrange[1], lonrange_map2[1] ], $
            LINESTYLE='Dotted', LABEL_POSITION=0, position = pos_map2, $
            center_longitude = center_longitude2,  /current,$
            GRID_LONGITUDE = lonlabelspacing, GRID_LATITUDE = latlabelspacing)
        map2.position = pos_map2 ;need to set this again for it to be set sometimes
        ;plot the continents
        mc = MAPCONTINENTS(/continents, FILL_COLOR='beige',/noclip)
        ;hide the necessary labels
        Map2_grid = Map2.mapgrid
        Map2_lats = Map2_grid.latitudes
        Map2_lons = Map2_grid.longitudes
        for i = 0, n_elements(Map2_lats)-1 do Map2_lats[i].label_show = 0
        Map2_lons[0].label_show = 0
    endif
    
    ;map1 and map2 positions
    actual_pos1 = map1.position
    ;get position of map2 if it exists
    if (map2 eq !NULL) then actual_pos2 = actual_pos1 else actual_pos2 = map2.position
    ;draw a box around the overall map
    border = polygon([actual_pos1[0],actual_pos2[2],actual_pos2[2],actual_pos1[0]],$
        [actual_pos1[1],actual_pos1[1],actual_pos1[3],actual_pos1[3]],$
        fill_background = 0,'black')
        
    ;if we have data specified to overplot
    if (keyword_set(xdata) AND keyword_set(xdata)) then begin
        ;if we only have one map, then only one call to plot is needed
        if (map2 ne !NULL) then begin
            ;separate data that goes to map 1 and map2
            map2_indices = where((xdata lt lonstart) OR (xdata gt 180.))
            p2 = plot(xdata[map2_indices]-360, ydata[map2_indices], current = map2,$
                /overplot, color = plot_color, thick = plot_thickness)
            
            ;add a polyline to the window so that we have connected plots, find closest
            ;data values to 180 degrees west

            point1=[max(xdata[map1_indices]), ydata[where(xdata eq max(xdata[map1_indices]))]]
            point2=[min(xdata[map2_indices])-360,ydata[where(xdata eq min(xdata[map2_indices]))]]

            ;convert locations to normalized coordinates after converting to cartesian X-Y
            pixelpoint1 = map1.convertcoord(map1.mapforward(point1), /DATA,  /TO_NORMAL)
            pixelpoint2 = map2.convertcoord(map2.mapforward(point2), /DATA,  /TO_NORMAL)

            ;make line between the two end points
            line = polyline([pixelpoint1[0], pixelpoint2[0]], [pixelpoint1[1], pixelpoint2[1]], $
                /DEVICE, target = w1, color = plot_color, thick = plot_thickness)
            line.position = [pixelpoint1[0], pixelpoint1[1], pixelpoint2[0], pixelpoint2[1]]
        endif
     endif

    ;refresh the overall plot and make window current
    w1.refresh
end


Reviewed by ZN (1-July-2015), JU (1-July-2015)