def add_lon_labels(wks): # # List the longitude values where you want labels. It's assumed that longitude=0 # is at the bottom of the plot, and 180W at the top. You can adjust as necessary. # lon_values = numpy.arange(-180, 180, 30) nlon = lon_values.shape[0] lat_values = numpy.zeros(nlon, 'f') + mpres.mpMinLatF + 0.005 # # Get the NDC coordinates of these lat,lon labels. # We'll use this information to place labels *outside* of # the map plot. # xndc, yndc = Ngl.datatondc(plot_base, lon_values, lat_values) # # Set an array of justification strings to use with the "txJust" resource # for each label, based on which quadrant it appears in. # just_strs = [ "BottomCenter", # top of plot "BottomRight", "BottomRight", # upper left quadrant "CenterRight", # left of plot "TopRight", "TopRight", # lower left quadrant "TopCenter", # bottom of plot "TopLeft", "TopLeft", # lower right quadrant "CenterLeft", # right of plot "BottomLeft", "BottomLeft" ] # upper right qudrant # Create an array of longitude labels with "W" and "E" added. lon_labels = [] for i in range(nlon): if lon_values[i] < 0: lon_labels.append("%gW" % abs(lon_values[i])) elif lon_values[i] > 0: lon_labels.append("%gE" % lon_values[i]) else: lon_labels.append("%g" % lon_values[i]) # Loop through each label and add it. txres = Ngl.Resources() txres.txFontHeightF = 0.01 for i in range(nlon): txres.txJust = just_strs[i] Ngl.text_ndc(wks, lon_labels[i], xndc[i], yndc[i], txres) return
npts = 400 x = Ngl.fspan(100., npts - 1, npts) y = 500. + x * numpy.sin(0.031415926535898 * x) wks = Ngl.open_wks("ps", "datandc") xy = Ngl.xy(wks, x, y) # # Test with all in-range values and no missing values present. # x_dat = x y_dat = numpy.absolute(y) x_ndc, y_ndc = Ngl.datatondc(xy, x_dat, y_dat) x_dat2, y_dat2 = Ngl.ndctodata(xy, x_ndc, y_ndc) check_type(x_ndc) check_type(y_ndc) check_type(x_dat2) check_type(y_dat2) test_values("no oor or msg data: datatondc/ndctodata", x_dat, x_dat2, delta=1e-4) test_values("no oor or msg data: datatondc/ndctodata", y_dat, y_dat2, delta=1e-4)
def trj(wks, x, y, time, res=None): ''' Plots trajectories with arrow heads attached. plot = trj(wks, x, y, time, res=None) wks : The identifier returned from calling Ngl.open_wks. x,y : The X and Y coordinates of the curve(s). These values can be one-dimensional NumPy arrays, NumPy masked arrays or two-dimensional NumPy arrays. If x and/or y are two-dimensional, then the leftmost dimension determines the number of curves. time : Time coordinates of the trajectories. These values can be one-dimensional NumPy arrays, NumPy masked arrays res: Resource list using following special resources: Special resources: trjArrowStep : Number of samples between arrow heads. Default: 10 trjArrowOffsetF : Shift the position of the arrows along the curve. Should be between 0. and 1. Default: 0.5 trjArrowDirection : can be 'pos' or 'neg' to select direction of the arrows. Default: 'pos' trjArrowXShapeF : Array containing the x NDC coordinates of the arrow head relative to the heads centre. Default: Equiliteral triangle trjArrowYShapeF : Array containing the y NDC coordinates of the arrow head relative to the heads centre. Default: Equiliteral triangle trjArrowXScaleF : Scales arrow head in X direction, befor rotation is applied. Default: 1. trjArrowYScaleF : Scales arrow head in y direction, befor rotation is applied. Default: 1. trjArrowSizeF : Scales the size of an arrow head. Default: 0.02 The arrow heads are plotted with add_polygon, so all gs* attributes of the resource list are applied to the arrow heads polygon. ''' if not res: res = ngl.Resources() # set default values: if not hasattr(res, 'trjArrowStep'): res.trjArrowStep = 10 if not hasattr(res, 'trjArrowOffsetF'): res.trjArrowOffsetF = .5 else: res.trjArrowOffsetF -= np.floor(res.trjArrowOffsetF) if not hasattr(res, 'trjArrowDirection'): res.trjArrowDirection = 'pos' if not hasattr(res, 'trjArrowXShapeF'): res.trjArrowXShapeF = [np.sqrt(3) / 3., -np.sqrt(3) / 6., -np.sqrt(3) / 6.] res.trjArrowXShapeF = np.asarray(res.trjArrowXShapeF) if not hasattr(res, 'trjArrowYShapeF'): res.trjArrowYShapeF = [0., .5, -.5] res.trjArrowYShapeF = np.asarray(res.trjArrowYShapeF) if not hasattr(res, 'trjArrowXScaleF'): res.trjArrowXScaleF = 1. if not hasattr(res, 'trjArrowYScaleF'): res.trjArrowYScaleF = 1. if not hasattr(res, 'trjArrowSizeF'): res.trjArrowSizeF = .02 # check for draw and frame if hasattr(res, "nglDraw"): doDraw = res.nglDraw else: doDraw = True res.nglDraw = False if hasattr(res, "nglFrame"): doFrame = res.nglFrame else: doFrame = True res.nglFrame = False # Convert to mask array x = np.ma.asarray(x) y = np.ma.asarray(y) time = np.ma.asarray(time) # check input data if x.shape != y.shape: raise ValueError("Inconsistend shape. x, y and time must have the " + "same shape.") if x.ndim < 1 or x.ndim > 2: raise ValueError("Input arrays x and y must be of rank 1 or 2.") if np.rank(x) == 1: # add singleton dimension to the begining x = x[np.newaxis, ...] y = y[np.newaxis, ...] # Dimension of trajectories dim = 0 # mask all missig values np.ma.masked_where(y.mask, x, copy=False) np.ma.masked_where(x.mask, y, copy=False) # create line plot resource res_lines = copy.deepcopy(res) # remove trj* attributes from res_lines for attr in dir(res_lines): if attr[0:3] == "trj" or (attr[0:2] == "gs" and attr[2] != "n"): delattr(res_lines, attr) # create line plot plot = ngl.xy(wks, x, y, res_lines) # get axes length in data coordinates xAxisLen, yAxisLen = [ngl.get_float(plot, "tr" + ax + "MaxF") - ngl.get_float(plot, "tr" + ax + "MinF") for ax in "XY"] # get marker color # to be implemented # place Marker marker_id = [] for t in xrange(x.shape[dim]): xt = x[t, ...].compressed() yt = y[t, ...].compressed() tt = time[::res.trjArrowStep] # shift time by offset if res.trjArrowOffsetF != 0.: tt = tt[:-1] + res.trjArrowOffsetF * (tt[1:] - tt[:-1]) # itterate over markers for tm in tt: # find nearest indices in time array idx = (np.abs(time - tm)).argmin().min() if time[idx] < tm: idx1 = idx idx2 = idx + 1 elif time[idx] > tm: idx1 = idx - 1 idx2 = idx else: if idx == 0: idx1 = idx idx2 = idx + 1 else: idx1 = idx - 1 idx2 = idx if idx >= len(xt) - 1: continue # interpolate linearly to get coordinates ds = (tm - time[idx1]) / (time[idx2] - time[idx1]) xm, ym = [coord[idx1] + ds * (coord[idx2] - coord[idx1]) for coord in [xt, yt]] x1, y1 = ngl.datatondc(plot, xt[idx1], yt[idx1]) x2, y2 = ngl.datatondc(plot, xt[idx2], yt[idx2]) angle = np.arctan2(y2 - y1, x2 - x1) # create marker resource res_marker = copy.deepcopy(res) # scale adjust marker scale res_marker.trjArrowXScaleF = res.trjArrowXScaleF * xAxisLen res_marker.trjArrowYScaleF = res.trjArrowYScaleF * yAxisLen marker_id.append( _plot_trj_marker(wks, plot, xm, ym, angle, res_marker)) if doDraw: ngl.draw(plot) res.nglDraw = True if doFrame: ngl.frame(wks) res.nglFrame = True return plot
def _plot_trj_marker(wks, plot, x, y, angle, res): ''' Plots a single arrow head onto the a trajectory plot pgon = _plot_trj_marker(wks, plot, x, y, angle, res) wks : The identifier returned from calling Ngl.open_wks. x,y : The X and Y coordinates of the arrow heads centre. xMarker, yMarker : X and Y coordinates in data coordinates of the marker polygon angle: angle relative to the x axis of the marker res: Resource list using following special resources: Special resources: trjArrowDirection : can be 'pos' or 'neg' to select direction of the arrows trjArrowXShapeF : Array containing the x coordinates of the arrow head relative to the heads centre. trjArrowYShapeF : Array containing the y coordinates of the arrow head relative to the heads centre. ''' # get plot centre in data coordinates xMid, yMid = [(ngl.get_float(plot, "tr" + ax + "MaxF") + ngl.get_float(plot, "tr" + ax + "MinF")) / 2. for ax in "XY"] # rotate arrow if res.trjArrowDirection == "neg": angle = angle - np.pi xMarker = copy.deepcopy(res.trjArrowXShapeF) yMarker = copy.deepcopy(res.trjArrowYShapeF) # scale marker xMarker = xMarker * res.trjArrowXScaleF * res.trjArrowSizeF + xMid yMarker = yMarker * res.trjArrowYScaleF * res.trjArrowSizeF + yMid xMarker_ndc, yMarker_ndc = ngl.datatondc(plot, xMarker, yMarker) # move centre of mass to origin xMarker_ndc, yMarker_ndc = [xMarker_ndc - np.mean(xMarker_ndc), yMarker_ndc - np.mean(yMarker_ndc)] # rotate marker xMarker_ndc, yMarker_ndc = [ np.cos(angle) * xMarker_ndc - np.sin(angle) * yMarker_ndc, np.sin(angle) * xMarker_ndc + np.cos(angle) * yMarker_ndc] # shift to final position xOffset_ndc, yOffset_ndc = ngl.datatondc(plot, x, y) xMarker_ndc += xOffset_ndc yMarker_ndc += yOffset_ndc # convert back to coordinates xMarker, yMarker = ngl.ndctodata(plot, xMarker_ndc, yMarker_ndc) # filter attributes from res for attr in dir(res): if not attr[0:2] in ["gs", "__"] or attr[0:3] == "gsn": delattr(res, attr) return ngl.add_polygon(wks, plot, xMarker, yMarker, res)
def add_labels_lcm(wks,map,dlat,dlon): PI = 3.14159 RAD_TO_DEG = 180./PI #-- determine whether we are in northern or southern hemisphere if (float(minlat) >= 0. and float(maxlat) > 0.): HEMISPHERE = "NH" else: HEMISPHERE = "SH" #-- pick some "nice" values for the latitude labels. lat_values = np.arange(int(minlat),int(maxlat),10) lat_values = lat_values.astype(float) nlat = len(lat_values) #-- We need to get the slope of the left and right min/max longitude lines. #-- Use NDC coordinates to do this. lat1_ndc = 0. lon1_ndc = 0. lat2_ndc = 0. lon2_ndc = 0. lon1_ndc,lat1_ndc = Ngl.datatondc(map,minlon,lat_values[0]) lon2_ndc,lat2_ndc = Ngl.datatondc(map,minlon,lat_values[nlat-1]) slope_lft = (lat2_ndc-lat1_ndc)/(lon2_ndc-lon1_ndc) lon1_ndc,lat1_ndc = Ngl.datatondc(map,maxlon,lat_values[0]) lon2_ndc,lat2_ndc = Ngl.datatondc(map,maxlon,lat_values[nlat-1]) slope_rgt = (lat2_ndc-lat1_ndc)/(lon2_ndc-lon1_ndc) #-- set some text resources txres = Ngl.Resources() txres.txFontHeightF = 0.01 txres.txPosXF = 0.1 #-- Loop through lat values, and attach labels to the left and right edges of #-- the masked LC plot. The labels will be rotated to fit the line better. dum_lft = [] #-- assign arrays dum_rgt = [] #-- assign arrays lat_label_lft = [] #-- assign arrays lat_label_rgt = [] #-- assign arrays for n in range(0,nlat): #-- left label if(HEMISPHERE == "NH"): rotate_val = -90. direction = "N" else: rotate_val = 90. direction = "S" #-- add extra white space to labels lat_label_lft.append("{}~S~o~N~{} ".format(str(np.abs(lat_values[n])),direction)) lat_label_rgt.append(" {}~S~o~N~{}".format(str(np.abs(lat_values[n])),direction)) txres.txAngleF = RAD_TO_DEG * np.arctan(slope_lft) + rotate_val dum_lft.append(Ngl.add_text(wks,map,lat_label_lft[n],minlon,lat_values[n],txres)) #-- right label if(HEMISPHERE == "NH"): rotate_val = 90 else: rotate_val = -90 txres.txAngleF = RAD_TO_DEG * np.arctan(slope_rgt) + rotate_val dum_rgt.append(Ngl.add_text(wks,map,lat_label_rgt[n],maxlon,lat_values[n],txres)) #---------------------------------------------------------------------- # Now do longitude labels. These are harder because we're not adding # them to a straight line. # Loop through lon values, and attach labels to the bottom edge for # northern hemisphere, or top edge for southern hemisphere. #---------------------------------------------------------------------- del(txres.txPosXF) txres.txPosYF = -5.0 #-- pick some "nice" values for the longitude labels lon_values = np.arange(int(minlon+10),int(maxlon-10),10).astype(float) lon_values = np.where(lon_values > 180, 360-lon_values, lon_values) nlon = lon_values.size dum_bot = [] #-- assign arrays lon_labels = [] #-- assign arrays if(HEMISPHERE == "NH"): lat_val = minlat else: lat_val = maxlat ctrl = "~C~" for n in range(0,nlon): if(lon_values[n] < 0): if(HEMISPHERE == "NH"): lon_labels.append("{}~S~o~N~W{}".format(str(np.abs(lon_values[n])),ctrl)) else: lon_labels.append("{}{}~S~o~N~W".format(ctrl,str(np.abs(lon_values[n])))) elif(lon_values[n] > 0): if(HEMISPHERE == "NH"): lon_labels.append("{}~S~o~N~E{}".format(str(lon_values[n]),ctrl)) else: lon_labels.append("{}{}~S~o~N~E".format(ctrl,str(lon_values[n]))) else: if(HEMISPHERE == "NH"): lon_labels.append("{}0~S~o~N~{}".format(ctrl,ctrl)) else: lon_labels.append("{}0~S~o~N~{}".format(ctrl,ctrl)) #-- For each longitude label, we need to figure out how much to rotate #-- it, so get the approximate slope at that point. if(HEMISPHERE == "NH"): #-- add labels to bottom of LC plot lon1_ndc,lat1_ndc = Ngl.datatondc(map, lon_values[n]-0.5, minlat) lon2_ndc,lat2_ndc = Ngl.datatondc(map, lon_values[n]+0.5, minlat) txres.txJust = "TopCenter" else: #-- add labels to top of LC plot lon1_ndc,lat1_ndc = Ngl.datatondc(map, lon_values[n]+0.5, maxlat) lon2_ndc,lat2_ndc = Ngl.datatondc(map, lon_values[n]-0.5, maxlat) txres.txJust = "BottomCenter" slope_bot = (lat1_ndc-lat2_ndc)/(lon1_ndc-lon2_ndc) txres.txAngleF = RAD_TO_DEG * np.arctan(slope_bot) #-- attach to map dum_bot.append(Ngl.add_text(wks, map, str(lon_labels[n]), \ lon_values[n], lat_val, txres)) return
wks_type = "png" wks = Ngl.open_wks(wks_type, "datatondc1") res = Ngl.Resources() res.nglMaximize = False res.vpXF = 0.1 res.vpYF = 0.9 res.vpHeightF = 0.8 res.vpWidthF = 0.8 xy = Ngl.xy(wks, x, y, res) x_in = x y_in = numpy.absolute(y) x_out, y_out = Ngl.datatondc(xy, x_in, y_in) # # Print out a table of data versus NDC values. # # for i in xrange(len(x_out)): # print("%4d data: (%3.0f, %8.4f) NDC: (%6.4f, %6.4f)" % \ # (i,x_in[i],y_in[i],x_out[i],y_out[i])) # # Draw some markers on the curve in the above plot. # Ngl.draw(xy) # Redraw the plot to start with. # # Then draw markers.