;***********************************************************************; ; Function : gsn_attach_plots ; ; base : base plot ; ; plots : list of plots to attach ; ; resplot1 : logical ; ; resplot2 : logical ; ; ; ; This function attaches the list of "plots" to the "base" plot, either; ; on the right Y axis or bottom X axis of the base plot. The default is ; ; to attach the plots at the Y axis, unless gsnAttachPlotsXAxis is set ; ; to True. ; ; ; ; By default, the viewport heights of all plots will be made the same, ; ; appropriate tick marks and labels will be turned off, and the aspect ; ; ratio preserved. ; ; ; ; For example, if you have the following plots and you want them ; ; attached at the Y axis: ; ; ; ; ___________ _____ __________ ; ; | | | | | | ; ; | | | | | | ; ; | base | | | | | ; ; | | | | | | ; ; | | | | | | ; ; ----------- ----- ---------- ; ; ; ; you will end up with: ; ; ; ; _________________________ ; ; | | | | ; ; | | | | ; ; | base | | | ; ; | | | | ; ; | | | | ; ; ------------------------- ; ; ; ; Or, if you have the following plots and you want them attached at the ; ; X axis: ; ; ; ; ___________ ___________ ; ; | | | | ; ; | | | | ; ; | base | | | ; ; | | ----------- ; ; | | ; ; ----------- ; ; ; ; you will end up with: ; ; ; ; ___________ ; ; | | ; ; | | ; ; | base | ; ; | | ; ; | | ; ; ----------- ; ; | | ; ; | | ; ; | | ; ; ----------- ; ; ; ; plotres1 and plotres2 are resources changing the default behavior of ; ; this function. ; ; ; ;***********************************************************************; undef("gsn_attach_plots") function gsn_attach_plots(oldbaseplot:graphic,oldplots:graphic, \ plotres1:logical, plotres2:logical) local anno, width1, width2, height1, height2, font_height1, font_height2, \ mj_length1, mj_length2, mjo_length1, mjo_length2, mno_length1, mno_length2, \ mno_length1, mno_length2, total_width1, total_width2, scale1, scale2, scale begin res1 = get_resources(plotres1) res2 = get_resources(plotres2) base = oldbaseplot plots = oldplots nplots= dimsizes(plots) res1@gsnMaximize = get_res_value(res1,"gsnMaximize",True) ;---Check for maximization maxbb = get_bb_res(res1) if(.not.maxbb) then maxbb = get_bb_res(res2) end if attach_y = .not.get_res_value(res1,"gsnAttachPlotsXAxis",False) attach_y = .not.get_res_value(res2,"gsnAttachPlotsXAxis",.not.attach_y) border_on = get_res_value(res1,"gsnAttachBorderOn",True) ; ; The plots to be attached may not be regular plots (contour, xy, vector, ; etc), so we won't be able to retrieve tickmark info from them. We have ; to see if they are overlay plots, instead, that have regular plots ; overlaid on them. If so, we can use the overlaid plots for tickmark ; info. If not, then we are in trouble. ; ; Here's the list of "regular" plot types: ; plot_types = (/"contourPlotClass","xyPlotClass","vectorPlotClass",\ "streamlinePlotClass","mapPlotClass","logLinPlotClass"/) ; ; First check the base plot for "regular plotness". ; found_base = False if(any(NhlClassName(base).eq.plot_types)) then ; ; The base plot is a regular plot. ; new_base = base found_base = True else ; ; The base plot is not a regular plot, so find out if it has a regular ; plot overlaid on it. ; getvalues base "pmOverlaySequenceIds" : base_ids end getvalues if(.not.ismissing(base_ids(0))) then j = 0 ; ; Loop through the overlaid plots and find a "regular" one. We will ; use the first one we find. ; do while(j.lt.dimsizes(base_ids).and..not.found_base) if(any(NhlClassName(base_ids(j)).eq.plot_types)) then new_base = base_ids(j) found_base = True end if j = j + 1 end do end if end if if(.not.found_base) then print("Warning: gsn_attach_plots: the base plot is an unrecognized plot type; may get unexpected results.") new_base = base end if getvalues new_base "trGridType" : grid_type end getvalues if(any(grid_type.eq.(/get_enum_value("Spherical"),\ get_enum_value("TriangularMesh")/))) then print("Warning: gsn_attach_plots: the base plot is either spherical or a triangular mesh, so can't fix the tickmarks") found_base = False end if ; ; Now test the plots to be attached, and see if they are "regular" plots. ; found_plots = new(nplots,logical) new_plots = new(nplots,graphic) found_plots = False do i=0,nplots-1 if(any(NhlClassName(plots(i)).eq.plot_types)) then new_plots(i) = plots(i) found_plots(i) = True else getvalues plots(i) "pmOverlaySequenceIds" : tmp_plot_ids end getvalues if(.not.ismissing(tmp_plot_ids(0))) then j = 0 ; ; Loop through the overlaid plots and find a "regular" one. We will ; use the first one we find. ; do while(j.lt.dimsizes(tmp_plot_ids).and..not.found_plots(i)) if(any(NhlClassName(tmp_plot_ids(j)).eq.plot_types)) then new_plots(i) = tmp_plot_ids(j) found_plots(i) = True end if j = j + 1 end do end if delete(tmp_plot_ids) end if if(.not.found_plots(i)) then print("Warning: gsn_attach_plots: unrecognized plot type, may get unexpected results.") new_plots(i) = plots(i) found_plots(i) = False end if end do ; ; Retrieve tickmark lengths and font height labels so we can make ; them the same size later. ; ; Also get the viewport widths and heights so we can maintain the ; aspect ratio, but yet make the heights or widths the same. ; getvalues base "vpWidthF" : width1 "vpHeightF" : height1 "tiMainFontHeightF" : main_font_height1 end getvalues widths = new(dimsizes(plots),float) heights = new(dimsizes(plots),float) do i=0,nplots-1 getvalues plots(i) "vpWidthF" : widths(i) "vpHeightF" : heights(i) end getvalues end do mj_lengths = new(nplots,float) mjo_lengths = new(nplots,float) mn_lengths = new(nplots,float) mno_lengths = new(nplots,float) font_heights = new(nplots,float) if(attach_y) ; ; If didn't find a regular base plot, then we can't do anything ; about the tickmarks. ; if(found_base) then getvalues new_base "tmXBMajorLengthF" : mj_length1 "tmXBMajorOutwardLengthF" : mjo_length1 "tmXBMinorLengthF" : mn_length1 "tmXBMinorOutwardLengthF" : mno_length1 "tmXBLabelFontHeightF" : font_height1 end getvalues end if do i=0,nplots-1 ; ; If didn't find a regular plot, then we can't do anything ; about the tickmarks. ; if(found_plots(i)) then getvalues new_plots(i) "tmXBMajorLengthF" : mj_lengths(i) "tmXBMajorOutwardLengthF" : mjo_lengths(i) "tmXBMinorLengthF" : mn_lengths(i) "tmXBMinorOutwardLengthF" : mno_lengths(i) "tmXBLabelFontHeightF" : font_heights(i) end getvalues end if end do else ; ; If didn't find a regular base plot, then we can't do anything ; about the tickmarks. ; if(found_base) then getvalues new_base "tmYLMajorLengthF" : mj_length1 "tmYLMajorOutwardLengthF" : mjo_length1 "tmYLMinorLengthF" : mn_length1 "tmYLMinorOutwardLengthF" : mno_length1 "tmYLLabelFontHeightF" : font_height1 end getvalues end if do i=0,nplots-1 if(found_plots(i)) then getvalues new_plots(i) "tmYLMajorLengthF" : mj_lengths(i) "tmYLMajorOutwardLengthF" : mjo_lengths(i) "tmYLMinorLengthF" : mn_lengths(i) "tmYLMinorOutwardLengthF" : mno_lengths(i) "tmYLLabelFontHeightF" : font_heights(i) end getvalues end if end do end if ; ; Calculate the scale factor needed to make the plots the same ; size in the appropriate axis. If we are attaching plots at the Y axis, ; then we want to make them the same height. Otherwise, we want to make ; them the same width. We do this by keeping the size of the largest ; plot the same, and scaling the rest of the plots to be the same height ; (or width). ; scales = new(nplots,float) if(attach_y) then if(any(height1.lt.heights)) then scale1 = max(heights)/height1 scales = max(heights)/heights else scale1 = 1. scales = height1/heights end if else if(any(width1.lt.widths)) then scale1 = max(widths)/width1 scales = max(widths)/widths else scale1 = 1. scales = width1/widths end if end if ; ; Because we are attaching plots along an axis, turn off ; tickmarks and labels where appropriate. ; if(attach_y) then if(found_base) then setvalues new_base "tmYUseLeft" : get_res_value_keep(res1,"tmYUseLeft",False) "tmYROn" : get_res_value_keep(res1,"tmYROn",False) "tmYRLabelsOn" : get_res_value_keep(res1,"tmYRLabelsOn",False) "tmYRBorderOn" : get_res_value_keep(res1,"tmYRBorderOn",border_on) end setvalues end if do i=0,nplots-2 if(found_plots(i)) then setvalues new_plots(i) "tmYUseLeft" : get_res_value_keep(res2,"tmYUseLeft",False) "tmYLOn" : get_res_value_keep(res2,"tmYLOn",False) "tmYLBorderOn" : get_res_value_keep(res2,"tmYLBorderOn",border_on) "tmYROn" : get_res_value_keep(res2,"tmYROn",False) "tmYRLabelsOn" : get_res_value_keep(res2,"tmYRLabelsOn",False) "tmYRBorderOn" : get_res_value_keep(res2,"tmYRBorderOn",border_on) "tiYAxisOn" : get_res_value_keep(res2,"tiYAxisOn",False) end setvalues end if end do if(found_plots(nplots-1)) then setvalues new_plots(nplots-1) "tmYUseLeft" : get_res_value_keep(res2,"tmYUseLeft",False) "tiYAxisOn" : get_res_value_keep(res2,"tiYAxisOn",False) "tmYLOn" : get_res_value_keep(res2,"tmYLOn",False) "tmYLBorderOn" : get_res_value_keep(res2,"tmYLBorderOn",border_on) "tmYLLabelsOn" : get_res_value_keep(res2,"tmYLLabelsOn",False) end setvalues end if else if(found_base) then setvalues new_base "tmXUseBottom" : get_res_value_keep(res1,"tmXUseBottom",False) "tmXBOn" : get_res_value_keep(res1,"tmXBOn",False) "tmXBBorderOn" : get_res_value_keep(res1,"tmXBBorderOn",border_on) "tmXBLabelsOn" : get_res_value_keep(res1,"tmXBLabelsOn",False) "tiXAxisOn" : get_res_value_keep(res1,"tiXAxisOn",False) end setvalues end if do i=0,nplots-2 if(found_plots(i)) then setvalues new_plots(i) "tmXUseBottom" : get_res_value_keep(res2,"tmXUseBottom",False) "tmXBOn" : get_res_value_keep(res2,"tmXBOn",False) "tmXBBorderOn" : get_res_value_keep(res2,"tmXBBorderOn",border_on) "tmXBLabelsOn" : get_res_value_keep(res2,"tmXBLabelsOn",False) "tmXTOn" : get_res_value_keep(res2,"tmXTOn",False) "tmXTBorderOn" : get_res_value_keep(res2,"tmXTBorderOn",border_on) "tmXTLabelsOn" : get_res_value_keep(res2,"tmXTLabelsOn",False) "tiMainOn" : get_res_value_keep(res2,"tiMainOn",False) "tiXAxisOn" : get_res_value_keep(res2,"tiXAxisOn",False) end setvalues end if end do if(found_plots(nplots-1)) then setvalues new_plots(nplots-1) "tmXUseBottom" : get_res_value_keep(res2,"tmXUseBottom",False) "tmXTOn" : get_res_value_keep(res2,"tmXTOn",False) "tmXTBorderOn" : get_res_value_keep(res2,"tmXTBorderOn",border_on) "tmXTLabelsOn" : get_res_value_keep(res2,"tmXTLabelsOn",False) "tiMainOn" : get_res_value_keep(res2,"tiMainOn",False) end setvalues end if end if ; ; Now that we've turned off the tickmark stuff, retrieve the bounding box ; of each plot. ; ; First create arrays to hold bounding box and viewport information. ; bbs = new((/nplots,4/),float) vpxs = new((/nplots/),float) vpys = new((/nplots/),float) vphs = new((/nplots/),float) vpws = new((/nplots/),float) bb1 = NhlGetBB(base) ; Get bounding box of plot top1 = bb1(0) bot1 = bb1(1) lft1 = bb1(2) rgt1 = bb1(3) ; ; Have to deal with special case of only having one plot. ; if(nplots.eq.1) bbs(0,:) = NhlGetBB(plots) else bbs = NhlGetBB(plots) end if tops = bbs(:,0) bots = bbs(:,1) lfts = bbs(:,2) rgts = bbs(:,3) ; ; Retrieve viewports. ; ; Calculate the largest scale factor possible that will allow us ; to fit all plots on the page, with 0.5% white space on the ends. ; getvalues base "vpYF" : vpy1 "vpHeightF" : vph1 "vpXF" : vpx1 "vpWidthF" : vpw1 end getvalues do i=0,nplots-1 getvalues plots(i) "vpYF" : vpys(i) "vpHeightF" : vphs(i) "vpXF" : vpxs(i) "vpWidthF" : vpws(i) end getvalues end do if(maxbb) then if(attach_y) then total_height1 = top1 - bot1 total_heights = tops - bots total_width1 = (vpx1+vpw1) - lft1 total_widths = vpws total_widths(nplots-1) = rgts(nplots-1) - vpxs(nplots-1) scale_widths = 1. / (1.01 * (scale1*total_width1 + sum(scales*total_widths))) scale_height1 = 1. / (1.01 * scale1*total_height1) scale_heights = 1. / (1.01 * scales*total_heights) scale = min((/scale_height1,min(scale_heights),min(scale_widths)/)) else total_width1 = rgt1 - lft1 total_widths = rgts - lfts total_height1 = vph1 + (top1 - vpy1) total_heights = vphs total_heights(nplots-1) = vpys(nplots-1)-bots(nplots-1) scale_heights = 1. / (1.01 * (scale1*total_height1 + sum(scales*total_heights))) scale_width1 = 1. / (1.01 * scale1*total_width1) scale_widths = 1. / (1.01 * scales*total_widths) scale = min((/scale_width1,min(scale_heights),min(scale_widths)/)) end if else scale = 1.0 end if ; ; Resize all plots with new scale factor, and set sizes of tick marks ; and tick marks labels to be the same. ; new_scale1 = scale * scale1 new_scales = scale * scales if(found_base) then new_mj_length = (new_scale1*mj_length1 + sum(new_scales*mj_lengths))/(nplots+1) new_mjo_length = (new_scale1*mjo_length1 + sum(new_scales*mjo_lengths))/(nplots+1) new_mn_length = (new_scale1*mn_length1 + sum(new_scales*mn_lengths))/(nplots+1) new_mno_length = (new_scale1*mno_length1 + sum(new_scales*mno_lengths))/(nplots+1) new_font_height = (new_scale1*font_height1 + sum(new_scales*font_heights))/(nplots+1) else new_font_height = sum(new_scales*font_heights)/nplots end if new_main_font_height = new_scale1*main_font_height1 if(found_base) then if(attach_y) then mj_length = get_res_value(res1,"tmXBMajorLengthF",new_mj_length) mjo_length = get_res_value(res1,"tmXBMajorOutwardLengthF",\ new_mjo_length) mn_length = get_res_value(res1,"tmXBMinorLengthF",new_mn_length) mno_length = get_res_value(res1,"tmXBMinorOutwardLengthF",\ new_mno_length) else mj_length = get_res_value(res1,"tmYLMajorLengthF",new_mj_length) mjo_length = get_res_value(res1,"tmYLMajorOutwardLengthF",\ new_mjo_length) mn_length = get_res_value(res1,"tmYLMinorLengthF",new_mn_length) mno_length = get_res_value(res1,"tmYLMinorOutwardLengthF",\ new_mno_length) end if end if font_heightxl = get_res_value(res1,"tmXBFontHeightF",new_font_height) font_heightyl = get_res_value(res1,"tmYLFontHeightF",new_font_height) font_heightx = get_res_value(res1,"tiXAxisFontHeightF",new_font_height) font_heighty = get_res_value(res1,"tiYAxisFontHeightF",new_font_height) main_font_height = get_res_value(res2,"tiMainFontHeightF", \ max((/new_main_font_height,new_font_height/))) setvalues base "vpHeightF" : new_scale1 * height1 "vpWidthF" : new_scale1 * width1 end setvalues if(found_base) then setvalues new_base "tiXAxisFontHeightF" : font_heightx "tiYAxisFontHeightF" : font_heighty "tiMainFontHeightF" : main_font_height "tmYRMajorLengthF" : mj_length "tmYRMajorOutwardLengthF" : mjo_length "tmYRMinorLengthF" : mn_length "tmYRMinorOutwardLengthF" : mno_length "tmYLMajorLengthF" : mj_length "tmYLMajorOutwardLengthF" : mjo_length "tmYLMinorLengthF" : mn_length "tmYLMinorOutwardLengthF" : mno_length "tmXBMajorLengthF" : mj_length "tmXBMajorOutwardLengthF" : mjo_length "tmXBMinorLengthF" : mn_length "tmXBMinorOutwardLengthF" : mno_length "tmXTMajorLengthF" : mj_length "tmXTMajorOutwardLengthF" : mjo_length "tmXTMinorLengthF" : mn_length "tmXTMinorOutwardLengthF" : mno_length "tmXBLabelFontHeightF" : font_heightxl "tmYLLabelFontHeightF" : font_heightyl end setvalues end if if(found_base) then if(attach_y) then mj_length = get_res_value(res2,"tmXBMajorLengthF",new_mj_length) mjo_length = get_res_value(res2,"tmXBMajorOutwardLengthF",\ new_mjo_length) mn_length = get_res_value(res2,"tmXBMinorLengthF",new_mn_length) mno_length = get_res_value(res2,"tmXBMinorOutwardLengthF",\ new_mno_length) else mj_length = get_res_value(res2,"tmYLMajorLengthF",new_mj_length) mjo_length = get_res_value(res2,"tmYLMajorOutwardLengthF",\ new_mjo_length) mn_length = get_res_value(res2,"tmYLMinorLengthF",new_mn_length) mno_length = get_res_value(res2,"tmYLMinorOutwardLengthF",\ new_mno_length) end if end if font_heightxl = get_res_value(res2,"tmXBFontHeightF",new_font_height) font_heightyl = get_res_value(res2,"tmYLFontHeightF",new_font_height) font_heightx = get_res_value(res2,"tiXAxisFontHeightF",new_font_height) font_heighty = get_res_value(res2,"tiYAxisFontHeightF",new_font_height) main_font_height = get_res_value(res2,"tiMainFontHeightF", \ max((/new_main_font_height,new_font_height/))) do i=0,nplots-1 setvalues plots(i) "vpHeightF" : new_scales(i) * heights(i) "vpWidthF" : new_scales(i) * widths(i) end setvalues if(found_plots(i)) then setvalues new_plots(i) "tiXAxisFontHeightF" : font_heightx "tiYAxisFontHeightF" : font_heighty "tiMainFontHeightF" : main_font_height end setvalues if(found_base) then setvalues new_plots(i) "tmYRMajorLengthF" : mj_length "tmYRMajorOutwardLengthF" : mjo_length "tmYRMinorLengthF" : mn_length "tmYRMinorOutwardLengthF" : mno_length "tmYLMajorLengthF" : mj_length "tmYLMajorOutwardLengthF" : mjo_length "tmYLMinorLengthF" : mn_length "tmYLMinorOutwardLengthF" : mno_length "tmXBMajorLengthF" : mj_length "tmXBMajorOutwardLengthF" : mjo_length "tmXBMinorLengthF" : mn_length "tmXBMinorOutwardLengthF" : mno_length "tmXTMajorLengthF" : mj_length "tmXTMajorOutwardLengthF" : mjo_length "tmXTMinorLengthF" : mn_length "tmXTMinorOutwardLengthF" : mno_length "tmXBLabelFontHeightF" : font_heightxl "tmYLLabelFontHeightF" : font_heightyl end setvalues end if end if end do ; ; Get new bounding boxes and sizes of resized plots, so we can ; figure out where to position the base plot. ; bb1 = NhlGetBB(base) ; Get bounding box of plot top1 = bb1(0) bot1 = bb1(1) lft1 = bb1(2) rgt1 = bb1(3) if(nplots.eq.1) bbs(0,:) = NhlGetBB(plots) else bbs = NhlGetBB(plots) end if tops = bbs(:,0) bots = bbs(:,1) lfts = bbs(:,2) rgts = bbs(:,3) getvalues base "vpYF" : vpy1 "vpHeightF" : vph1 "vpXF" : vpx1 "vpWidthF" : vpw1 end getvalues do i=0,nplots-1 getvalues plots(i) "vpYF" : vpys(i) "vpHeightF" : vphs(i) "vpXF" : vpxs(i) "vpWidthF" : vpws(i) end getvalues end do if(attach_y) then total_height1 = top1 - bot1 total_heights = tops - bots total_width1 = (vpx1+vpw1) - lft1 total_widths = vpws total_widths(nplots-1) = rgts(nplots-1) - vpxs(nplots-1) total_width_left = max((/0.,1. - (total_width1 + sum(total_widths))/)) total_height_left = max((/1. - max((/total_height1,max(total_heights)/))/)) else total_width1 = rgt1 - lft1 total_widths = rgts - lfts total_height1 = vph1 + (top1 - vpy1) total_heights = vphs total_heights(nplots-1) = vpys(nplots-1)-bots(nplots-1) total_height_left = max((/0.,1. - (total_height1 + sum(total_heights))/)) total_width_left = max((/0.,1. - max((/total_width1,max(total_widths)/))/)) end if new_vpx1 = total_width_left/2. + (vpx1-lft1) new_vpy1 = 1. - (total_height_left/2. + (top1-vpy1)) setvalues base "vpYF" : new_vpy1 "vpXF" : new_vpx1 end setvalues ; ; Attach each plot. If attaching them on the X axis, then start with ; the bottommost plot. If attaching on the Y axis, start with the ; rightmost plot. ; annos = new(nplots,graphic) zone = get_res_value(res2,"amZone",1) orth = get_res_value(res2,"amOrthogonalPosF",0.0) para = get_res_value(res2,"amParallelPosF",0.5) if(attach_y) then side = get_res_value(res2,"amSide","Right") just = get_res_value(res2,"amJust","CenterLeft") else side = get_res_value(res2,"amSide","Bottom") just = get_res_value(res2,"amJust","TopCenter") end if do i=nplots-1,0,1 if(i.gt.0) then annos(i) = NhlAddAnnotation(plots(i-1),plots(i)) else annos(0) = NhlAddAnnotation(base,plots(0)) end if setvalues annos(i) "amZone" : zone "amJust" : just "amSide" : side "amResizeNotify" : True ; Allow resize if plot resized. "amParallelPosF" : para "amOrthogonalPosF": orth end setvalues end do ;---The plot does not get drawn in this function! wks = NhlGetParentWorkstation(base) draw_and_frame(wks,base,False,False,0,maxbb) return(annos) end