Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Code modification
#1
Hi,

I have a Python script that generates a 3D plot from a Excel .csv file with three columns (Phi, Theta and Power). I'm pretty new with Python. Now I have to modify the code so that Phi have intervalls up to 360 and Theta up to 180. I have tried couple of times now, and cant see the problem. Basically...the logic og Theta and Phi have to be oposite. otherwies the code is working fine. Pretty sure I have to make a change in "deg read" or "def interpolate_x". I have tried to just switch the names og "Theta" and "Phi", but that does not help.
Any help would be strongly appreciated.

This is the code:

import pandas as pd
import click
import matplotlib
#matplotlib.use('TkAgg')
# This import registers the 3D projection, but is otherwise unused.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import griddata
import os
import ast

class PythonLiteralOption(click.Option):

    def type_cast_value(self, ctx, value):
        try:
            return ast.literal_eval(value)
        except:
            raise click.BadParameter(value)

def dbm_to_W(db):
    """
    Convert decibell-milliWatts to Watts
    :param db:
    :return:
    """
    return 10**(db/10)/1000


def read(filename):
    df = pd.read_csv(filename).sort_values(by = ['Theta','Phi'])
    phi = df['Phi'].to_numpy(dtype='float')
    theta = df['Theta'].to_numpy(dtype='float')
    r = df['Power'].to_numpy(dtype='float')
    return [r,theta,phi]


def interpolate_x(x, theta, phi, theta_resolution = 5, phi_resolution = 5, close_gap=True):
    """
    Apply bicubic interpolation.
    Assumptions: theta and phi are in degrees in limits according to ISO conventions
    Theta and phi values cover their respective domain, if
    this assumption doesn't hold, extrapolation would occur.
    :param x: data values
    :param theta: elevation values, must cover the 0-180 range
    :param phi: azimuth values, must cover the 0-360 range
    :param theta_resolution: output elevation resolution
    :param phi_resolution: output azimuth resolution
    :param close_gap: boolean, when true sets X[360] to X[0]
    :return: [X, THETA, PHI] - interpolated values of X and the THETA-PHI interpolation grid
    """
    THETA, PHI = np.mgrid[0.:180.000005:theta_resolution,
                         0:360.000005:phi_resolution]
    # X = interpolator(theta_range, phi_range)
    points=np.array([theta.ravel(), phi.ravel()]).T
    X = griddata(points, x.ravel(), (THETA, PHI), method='cubic')
    if close_gap:
        X[PHI == 360.] = X[PHI == 0.]
    return [X, THETA, PHI]

def arrange_levels(r,theta,phi):
    """
    This generates contour lines to be consumed by matplotlib
    surface plot. We rely on data being sorted by theta here.
    :param r:
    :param theta:
    :param phi:
    :return:
    """
    #TODO: maybe support different number of point in each contour
    #   using masked arrays? Sub below
    #
    # R = []
    # THETA = []
    # PHI = []
    # i = 0
    # begin = 0
    # # Not the most efficient implementation, but makes clear what's happening
    # # for i in range(len(theta)):
    # n = len(theta)
    # while True:
    #     t = curtheta = theta[i]
    #     while curtheta==t and i<n:
    #         t = theta[i]
    #         i+=1;
    #     THETA.append(theta[begin:i])
    #     PHI.append(phi[begin:i])
    #     R.append(r[begin:i])
    #     if i==n:
    #         break
    #
    # return (np.array(R),
    #         np.array(THETA),
    #         np.array(PHI))
    u = len(np.unique(theta))
    return [x.reshape((u,-1)) for x in (r, theta, phi)]


def sphere2cart(r, theta, phi):
    """
    Convert spherical polar coordinates to Cartesian coordinates according to
    ISO. See https://en.wikipedia.org/wiki/Spherical_coordinate_system#Conventions
    :param r:
    :param theta:
    :param phi:
    :return:
    """
    t = np.deg2rad(theta)
    p = np.deg2rad(phi)
    x = r*np.sin(t)*np.cos(p)
    y = r*np.sin(t)*np.sin(p)
    z = r*np.cos(t)
    return (x,y,z)


def _visuv(r,theta,phi):
    """
    A utility I used to get insight into data
    :param r:
    :param theta:
    :param phi:
    :return:
    """
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')

    # Create the mesh in polar coordinates and compute corresponding Z.

    # Plot the surface.
    ax.contour(theta,phi,r)
    ax.plot_surface(theta,phi,r)#, cmap=plt.cm.YlGnBu_r)
    ax.set_xlabel('theta')
    ax.set_ylabel('phi')
    ax.set_zlabel('r')
    plt.show()


def set_view_deg(ax,azim, elev):
    ax.view_init(azim=azim, elev = elev)
    ax.get_proj()
    ax.stale = True
    ax.figure.canvas.draw_idle()

def axisEqual3D(ax):
    """
    Helper to get correct aspect ratio in 3d plots
    https://stackoverflow.com/a/19248731/3791466
    :param ax:
    :return:
    """
    extents = np.array([getattr(ax, 'get_{}lim'.format(dim))() for dim in 'xyz'])
    sz = extents[:,1] - extents[:,0]
    centers = np.mean(extents, axis=1)
    maxsize = max(abs(sz))
    r = maxsize/2
    # ax.auto_scale_xyz(*np.column_stack((centers - r, centers + r)))
    for ctr, dim in zip(centers, 'xyz'):
        getattr(ax, 'set_{}lim'.format(dim))(ctr - r, ctr + r)

def add_3d_axes(ax):
    x, y, z = np.zeros((3, 3))
    u, v, w = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
    ax.quiver(x, y, z, u, v, w, arrow_length_ratio=0.1)
    for coords, text in zip((u,v,w),"xyz"):
        ax.text(*coords, text )# , zdir=None, **kwargs)

def save_standard_TI_notation(path, fig,ax,prefix=''):
    standard_rotations = {
        "90_0":(0,0),
        "0_0":(-90,90),
        "90_270":(-90,0),
        "90_90":(90,0),
        "90_180":(180,0),
        "180_0":(90,-90)
    }
    for r in standard_rotations:
        azim = standard_rotations[r][0]
        elev = standard_rotations[r][1]
        set_view_deg(ax,azim=azim,elev=elev)
        pythonFriendlyPath = path.replace('"\"', '"/"')
        filename = r+".png"
        filename = os.path.join(pythonFriendlyPath, filename)
        fig.savefig(filename)
        #fig.savefig(r+".png")


def visualize(path, X,Y,Z,V,cmap_limits = (0,0), prefix="", show=True, show_wireframe =False):

    if cmap_limits=='auto' or cmap_limits is None or cmap_limits[0]==cmap_limits[1]:
        (slope,offset) = get_autoscale(V,0,1)
        bounds=(V.min(),V.max())
    else:
        # cast to float to be safe
        bounds = np.array(cmap_limits,dtype='float')
        (slope,offset) = get_autoscale(bounds,0,1)


    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')#,proj_type = 'ortho')
    plt.tight_layout()
    plt.axis('scaled')

    cmap=plt.get_cmap('jet')
    patchcolors = cmap(rescale(V,slope,offset), alpha=1)
    surf = ax.plot_surface(X, Y, Z, facecolors=patchcolors,rstride=1, cstride=1, linewidth=0.5)#, cmap=plt.cm.YlGnBu_r)
    if show_wireframe:
        wf = ax.plot_wireframe(X, Y, Z,rstride=1, cstride=1, linewidth=0.5, color='k')#, cmap=plt.cm.YlGnBu_r)
    add_3d_axes(ax)
    # set_view_deg(ax,azim=30,elev=90)
    ax.set_xlim((-1,1))
    ax.set_ylim((-1,1))
    ax.set_zlim((-1,1))
    plt.axis('off')
    # cbax = fig.add_subplot(122)
    cbax = plt.axes((0.8,0.5, 0.05, 0.45))
    norm = matplotlib.colors.Normalize(vmin=bounds[0], vmax=bounds[1])

    cb1 = matplotlib.colorbar.ColorbarBase(cbax, cmap=cmap,
                                    norm=norm,
                                    orientation="vertical")
    save_standard_TI_notation(path, fig,ax,prefix=prefix)
    if show:
        plt.show()
    # axisEqual3D(ax)

    # import vispy.plot as vp
    # from vispy import color
    #
    # fig = vp.Fig()
    # ax = fig[0,0]
    # ax.surface(Z)

def rescale(x,slope, offset):
    return x*slope+offset

def get_autoscale(x, lower, upper):
    xmin = x.min()
    xmax = x.max()
    slope = (upper-lower)/(xmax-xmin)
    offset = upper - slope*xmax
    return (slope, offset)


@click.command(context_settings=dict(help_option_names=['-h', '--help']))
@click.option('--dbm_to_watts', is_flag=True, help="if present will convert dbm to watts")
@click.option('--disable_interpolation', is_flag=True , help="if present will disable interpolation with 5 degree resulution")
@click.option('--show/--no-show', default=True, help="whether to show the interactive window")
# @click.option('--cmap_limits', cls=PythonLiteralOption, default='"auto"')#,help="preset colormap limits")
@click.option('--cmap_limits', type=(float, float), default=(0,0))#,help="preset colormap limits")
@click.argument('filename', nargs=-1)#,help="paths to csv files to process")
@click.argument('path', nargs=1)#,help="path to where file should be saved")
def main(filename, path, dbm_to_watts = False, disable_interpolation=False, cmap_limits='auto', show=True):
    """
    :param filename:
    :param path:
    :param dbm_to_watts:
    :param disable_interpolation:
    :param cmap_limits: Limits for the colormap. Use 'auto' or a tuple of lower and upper bounds
        in data units.
    :return:
    """
    for f in filename:
        data = read(f)
        if dbm_to_watts:
            db = data[0] #save dbm to use in the legend adn coloring
            data[0] = dbm_to_W(db) #convert to Watts
        arranged = arrange_levels(*data)
        if disable_interpolation:
            interpolated = arranged
        else:
            interpolated= interpolate_x(*arranged)
        slope, offset= get_autoscale(interpolated[0], lower=0, upper=1)
        r = rescale(interpolated[0], slope, offset)
        xyz = sphere2cart(r,interpolated[1],interpolated[2])
        prefix = os.path.splitext(f)
        visualize(path, *xyz,interpolated[0],cmap_limits=cmap_limits,prefix=prefix, show=show)




if __name__=='__main__':
    main()
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Scaled scatter modification yvrob 1 1,972 Nov-08-2019, 04:05 AM
Last Post: yvrob
  Python script modification rajannn 1 1,913 Oct-06-2019, 07:55 PM
Last Post: micseydel
  List modification returns none Mark17 6 2,874 Sep-02-2019, 05:40 PM
Last Post: Mark17
  pyPDF2 nautilus columns modification AJBek 1 2,906 Jun-07-2019, 04:17 PM
Last Post: micseydel

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020