2021年1月2日星期六

Save Elements' Locations to local file after dragging & dropping

I was upgrading one of my projects which is a predictor of rebounding odds based on players' locations on the court. Here is the link: http://okc-thunder-rebounds.herokuapp.com/.

I wanted to make it more user-friendly by enabling dragging & dropping. I mean, users are able to place the items that represent the players anywhere in the court panel. Currently I had a hard time importing the final locations of players when they are placed to my model because previously I developed with Python and Streamlit while I am doing it in HTML, CSS, and JavaScript.

I am a newbie to the latter three so I couldn't find a way that is able to transport/import the data to my python codes directly. I guess it is possible to save the data first locally and then I can read them using python afterward.

So in short: how can I manage to record the locations of items (in pixels) as soon as they are placed and if possible, their records are able to be changed when they are moved?

Here is what I have done to make it drag & drop:

<html><head><style>  #red_button1 {    position: absolute;    top:10px;    left:20px;    z-index: 9;    background-color: #f1f1f1;    text-align: center;    border: 1px solid #d3d3d3;    display:block;   }    #def1 {    cursor: move;    z-index: 10;    background-color: #FF0000;    color: #000;    border: 1px solid #d3d3d3;  }    #red_button2 {    position: absolute;    top:60px;    left:20px;    z-index: 9;    background-color: #f1f1f1;    text-align: center;    border: 1px solid #d3d3d3;    display:block;   }    #def2 {    cursor: move;    z-index: 10;    background-color: #FF0000;    color: #000;    border: 1px solid #d3d3d3;  }    #red_button3 {    position: absolute;    top:110px;    left:20px;    z-index: 9;    background-color: #f1f1f1;    text-align: center;    border: 1px solid #d3d3d3;    display:block;   }    #def3 {    cursor: move;    z-index: 10;    background-color: #FF0000;    color: #000;    border: 1px solid #d3d3d3;  }    #red_button4 {    position: absolute;    top:160px;    left:20px;    z-index: 9;    background-color: #f1f1f1;    text-align: center;    border: 1px solid #d3d3d3;    display:block;   }    #def4 {    cursor: move;    z-index: 10;    background-color: #FF0000;    color: #000;    border: 1px solid #d3d3d3;  }    #red_button5 {    position: absolute;    top:210px;    left:20px;    z-index: 9;    background-color: #f1f1f1;    text-align: center;    border: 1px solid #d3d3d3;    display:block;   }    #def5 {    cursor: move;    z-index: 10;    background-color: #FF0000;    color: #000;    border: 1px solid #d3d3d3;  }    #blue_button1 {    position: absolute;    top:10px;    left:60px;    z-index: 9;    background-color: #f1f1f1;    text-align: center;    border: 1px solid #d3d3d3;    display:block;   }    #off1 {    cursor: move;    z-index: 10;    background-color: #0000FF;    color: #000;    border: 1px solid #d3d3d3;   }    #blue_button2 {    position: absolute;    top:60px;    left:60px;    z-index: 9;    background-color: #f1f1f1;    text-align: center;    border: 1px solid #d3d3d3;    display:block;   }    #off2 {    cursor: move;    z-index: 10;    background-color: #0000FF;    color: #000;    border: 1px solid #d3d3d3;   }    #blue_button3 {    position: absolute;    top:110px;    left:60px;    z-index: 9;    background-color: #f1f1f1;    text-align: center;    border: 1px solid #d3d3d3;    display:block;   }    #off3 {    cursor: move;    z-index: 10;    background-color: #0000FF;    color: #000;    border: 1px solid #d3d3d3;   }    #blue_button4 {    position: absolute;    top:160px;    left:60px;    z-index: 9;    background-color: #f1f1f1;    text-align: center;    border: 1px solid #d3d3d3;    display:block;   }    #off4 {    cursor: move;    z-index: 10;    background-color: #0000FF;    color: #000;    border: 1px solid #d3d3d3;   }    #blue_button5 {    position: absolute;    top:210px;    left:60px;    z-index: 9;    background-color: #f1f1f1;    text-align: center;    border: 1px solid #d3d3d3;    display:block;   }    #off5 {    cursor: move;    z-index: 10;    background-color: #0000FF;    color: #000;    border: 1px solid #d3d3d3;   }    #panel {      width: 350px;      height: 350px;      background-size: contain;  }    #display {    position: absolute;    top:500px;    left:10px;    z-index: 9;    background-color: #f1f1f1;    text-align: center;    border: 1px solid #d3d3d3;    display:block;     width: 100px;  }  </style>  </head><body>  <div id="panel" style="background-image: url('court.png');">  <div id="red_button1">      <div id="def1">1</div>  </div>  <div id="red_button2">      <div id="def2">2</div>  </div>  <div id="red_button3">      <div id="def3">3</div>  </div>  <div id="red_button4">      <div id="def4">4</div>  </div>  <div id="red_button5">      <div id="def5">5</div>  </div>  <div id="blue_button1">      <div id="off1" style="color:white">1</div>  </div>  <div id="blue_button2">      <div id="off2" style="color:white">2</div>  </div>  <div id="blue_button3">      <div id="off3" style="color:white">3</div>  </div>  <div id="blue_button4">      <div id="off4" style="color:white">4</div>  </div>  &lt;<div id="blue_button5">      <div id="off5" style="color:white">5</div>  </div>  </div>  <input type="button" value="Show Positions" onclick="SHowDiv()">  <div id="display"></div>  <script>  //Make the DIV elements draggagle:  dragElement(document.getElementById("red_button1"));  dragElement(document.getElementById("red_button2"));  dragElement(document.getElementById("red_button3"));  dragElement(document.getElementById("red_button4"));  dragElement(document.getElementById("red_button5"));  dragElement(document.getElementById("blue_button1"));  dragElement(document.getElementById("blue_button2"));  dragElement(document.getElementById("blue_button3"));  dragElement(document.getElementById("blue_button4"));  dragElement(document.getElementById("blue_button5"));    //output their final locations  // getOffset(document.getElementById("red_button1"));  // getOffset(document.getElementById("red_button2"));  // getOffset(document.getElementById("red_button3"));  // getOffset(document.getElementById("red_button4"));  // getOffset(document.getElementById("red_button5"));  // getOffset(document.getElementById("blue_button1"));  // getOffset(document.getElementById("blue_button2"));  // getOffset(document.getElementById("blue_button3"));  // getOffset(document.getElementById("blue_button4"));  // getOffset(document.getElementById("blue_button5"));    function dragElement(elmnt) {    var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;    elmnt.onmousedown = dragMouseDown;      function dragMouseDown(e) {      e = e || window.event;      e.preventDefault();      // get the mouse cursor position at startup:      pos3 = e.clientX;      pos4 = e.clientY;      document.onmouseup = closeDragElement;      // call a function whenever the cursor moves:      document.onmousemove = elementDrag;    }      function elementDrag(e) {      e = e || window.event;      e.preventDefault();      // calculate the new cursor position:      pos1 = pos3 - e.clientX;      pos2 = pos4 - e.clientY;      pos3 = e.clientX;      pos4 = e.clientY;      // set the element's new position:      elmnt.style.top = (elmnt.offsetTop - pos2) + "px";      elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";    }      function closeDragElement() {      /* stop moving when mouse button is released:*/      document.onmouseup = null;      document.onmousemove = null;    }  }    function getOffset(elmnt) {      return {top: elmnt.style.top, left: elmnt.style.left};  }    String.prototype.trimRight = function(charlist) {    if (charlist === undefined)      charlist = "\s";      return this.replace(new RegExp("[" + charlist + "]+$"), "");  };    function SHowDiv()  {    document.getElementById("display").innerHTML =     getOffset(document.getElementById("red_button1")).top.trimRight("px") + ", " + getOffset(document.getElementById("red_button1")).left.trimRight("px") + "<br>" +     getOffset(document.getElementById("red_button2")).top.trimRight("px") + ", " + getOffset(document.getElementById("red_button2")).left.trimRight("px") + "<br>" +     getOffset(document.getElementById("red_button3")).top.trimRight("px") + ", " + getOffset(document.getElementById("red_button3")).left.trimRight("px") + "<br>" +     getOffset(document.getElementById("red_button4")).top.trimRight("px") + ", " + getOffset(document.getElementById("red_button4")).left.trimRight("px") + "<br>" +     getOffset(document.getElementById("red_button5")).top.trimRight("px") + ", " + getOffset(document.getElementById("red_button5")).left.trimRight("px") + "<br>" +     getOffset(document.getElementById("blue_button1")).top.trimRight("px") + ", " + getOffset(document.getElementById("blue_button1")).left.trimRight("px") + "<br>" +     getOffset(document.getElementById("blue_button2")).top.trimRight("px") + ", " + getOffset(document.getElementById("blue_button2")).left.trimRight("px") + "<br>" +     getOffset(document.getElementById("blue_button3")).top.trimRight("px") + ", " + getOffset(document.getElementById("blue_button3")).left.trimRight("px") + "<br>" +     getOffset(document.getElementById("blue_button4")).top.trimRight("px") + ", " + getOffset(document.getElementById("blue_button4")).left.trimRight("px") + "<br>" +     getOffset(document.getElementById("blue_button5")).top.trimRight("px") + ", " + getOffset(document.getElementById("blue_button5")).left.trimRight("px");  }    </script>        </body></html>  

And here is the code of my original work:

import streamlit as st  from joblib import load  import seaborn as sns  import pandas as pd  import numpy as np  import matplotlib.pyplot as plt  from matplotlib.patches import Circle, Rectangle, Arc  import plotly  from plotly.subplots import make_subplots  import plotly.graph_objects as go      # draw the half court  def create_half_court(ax=None, three_line='mens', court_color='#dfbb85',                        lw=3, lines_color='black', lines_alpha=0.5,                        paint_fill='blue', paint_alpha=0.4,                        inner_arc=False):      """      Version 2020.2.19        Creates NBA Basketball Half Court      Dimensions are in feet (Court is 97x50 ft)      Created by: Rob Mulla / https://github.com/RobMulla        * Note that this function uses "feet" as the unit of measure.      * Our data is within this range: -47 <= x <= 47, -25 <= y <= 25.      * So to plot X/Y positions first convert to feet like this:      ```      loc['loc_x_'] = 47 - loc['loc_x_']      loc['loc_y_'] = 25 - loc['loc_y_']      ```      ax: matplotlib axes if None gets current axes using `plt.gca`            three_line: 'mens', 'womens' or 'both' defines 3 point line plotted      court_color : (hex) Color of the court      lw : line width      lines_color : Color of the lines      lines_alpha : transparency of lines      paint_fill : Color inside the paint      paint_alpha : transparency of the "paint"      inner_arc : paint the dotted inner arc      """      if ax is None:          ax = plt.gca()        # Create Pathes for Court Lines      center_circle = Circle((50 / 2, 94 / 2), 6,                             linewidth=lw, color=lines_color, lw=lw,                             fill=False, alpha=lines_alpha)      hoop = Circle((50 / 2, 5.25), 1.5 / 2,                    linewidth=lw, color=lines_color, lw=lw,                    fill=False, alpha=lines_alpha)        # Paint - 18 Feet 10 inches which converts to 18.833333 feet - gross!      paint = Rectangle(((50 / 2) - 6, 0), 12, 18.833333,                        fill=paint_fill, alpha=paint_alpha,                        lw=lw, edgecolor=None)        paint_boarder = Rectangle(((50 / 2) - 6, 0), 12, 18.833333,                                fill=False, alpha=lines_alpha,                                lw=lw, edgecolor=lines_color)        arc = Arc((50 / 2, 18.833333), 12, 12, theta1=-      0, theta2=180, color=lines_color, lw=lw,                alpha=lines_alpha)        block1 = Rectangle(((50 / 2) - 6 - 0.666, 7), 0.666, 1,                         fill=True, alpha=lines_alpha,                         lw=0, edgecolor=lines_color,                         facecolor=lines_color)      block2 = Rectangle(((50 / 2) + 6, 7), 0.666, 1,                         fill=True, alpha=lines_alpha,                         lw=0, edgecolor=lines_color,                         facecolor=lines_color)      ax.add_patch(block1)      ax.add_patch(block2)        l1 = Rectangle(((50 / 2) - 6 - 0.666, 11), 0.666, 0.166,                     fill=True, alpha=lines_alpha,                     lw=0, edgecolor=lines_color,                     facecolor=lines_color)      l2 = Rectangle(((50 / 2) - 6 - 0.666, 14), 0.666, 0.166,                     fill=True, alpha=lines_alpha,                     lw=0, edgecolor=lines_color,                     facecolor=lines_color)      l3 = Rectangle(((50 / 2) - 6 - 0.666, 17), 0.666, 0.166,                     fill=True, alpha=lines_alpha,                     lw=0, edgecolor=lines_color,                     facecolor=lines_color)      ax.add_patch(l1)      ax.add_patch(l2)      ax.add_patch(l3)      l4 = Rectangle(((50 / 2) + 6, 11), 0.666, 0.166,                     fill=True, alpha=lines_alpha,                     lw=0, edgecolor=lines_color,                     facecolor=lines_color)      l5 = Rectangle(((50 / 2) + 6, 14), 0.666, 0.166,                     fill=True, alpha=lines_alpha,                     lw=0, edgecolor=lines_color,                     facecolor=lines_color)      l6 = Rectangle(((50 / 2) + 6, 17), 0.666, 0.166,                     fill=True, alpha=lines_alpha,                     lw=0, edgecolor=lines_color,                     facecolor=lines_color)      ax.add_patch(l4)      ax.add_patch(l5)      ax.add_patch(l6)        # 3 Point Line      if (three_line == 'mens') | (three_line == 'both'):          # 22' 1.75" distance to center of hoop          three_pt = Arc((50 / 2, 6.25), 44.291, 44.291, theta1=12,                         theta2=168, color=lines_color, lw=lw,                         alpha=lines_alpha)            # 4.25 feet max to sideline for mens          ax.plot((3.34, 3.34), (0, 11.20),                  color=lines_color, lw=lw, alpha=lines_alpha)          ax.plot((50 - 3.34, 50 - 3.34), (0, 11.20),                  color=lines_color, lw=lw, alpha=lines_alpha)          ax.add_patch(three_pt)        if (three_line == 'womens') | (three_line == 'both'):          # womens 3          three_pt_w = Arc((50 / 2, 6.25), 20.75 * 2, 20.75 * 2, theta1=5,                           theta2=175, color=lines_color, lw=lw, alpha=lines_alpha)          # 4.25 inches max to sideline for mens          ax.plot((4.25, 4.25), (0, 8), color=lines_color,                  lw=lw, alpha=lines_alpha)          ax.plot((50 - 4.25, 50 - 4.25), (0, 8.1),                  color=lines_color, lw=lw, alpha=lines_alpha)            ax.add_patch(three_pt_w)        # Add Patches      ax.add_patch(paint)      ax.add_patch(paint_boarder)      ax.add_patch(center_circle)      ax.add_patch(hoop)      ax.add_patch(arc)        if inner_arc:          inner_arc = Arc((50 / 2, 18.833333), 12, 12, theta1=180,                          theta2=0, color=lines_color, lw=lw,                          alpha=lines_alpha, ls='--')          ax.add_patch(inner_arc)        # Restricted Area Marker      restricted_area = Arc((50 / 2, 6.25), 8, 8, theta1=0,                            theta2=180, color=lines_color, lw=lw,                            alpha=lines_alpha)      ax.add_patch(restricted_area)        # Backboard      ax.plot(((50 / 2) - 3, (50 / 2) + 3), (4, 4),              color=lines_color, lw=lw * 1.5, alpha=lines_alpha)      ax.plot((50 / 2, 50 / 2), (4.3, 4), color=lines_color,              lw=lw, alpha=lines_alpha)        # Half Court Line      ax.axhline(94 / 2, color=lines_color, lw=lw, alpha=lines_alpha)        # Plot Limit      ax.set_xlim(0, 50)      ax.set_ylim(0, 94 / 2 + 2)      ax.set_facecolor(court_color)      ax.set_xticks([])      ax.set_yticks([])      ax.set_xlabel('')      return ax      def slider_setting(condition, position):      x = st.sidebar.slider('Coordinate X for ' + condition + ' Player ' + str(position), min_value=-47., max_value=47.,                            value=0., step=0.1)      y = st.sidebar.slider('Coordinate Y for ' + condition + ' Player ' + str(position), min_value=-25., max_value=25.,                            value=0., step=0.1)      return x, y      def locate(locations, court):      off_ = pd.DataFrame({          'X': [47 - location[0] if location[0] >= 0 else 47 + location[0] for location in locations[:5]],          'Y': [25 - location[1] for location in locations[:5]]          })      def_ = pd.DataFrame({          'X': [47 - location[0] if location[0] >= 0 else 47 + location[0] for location in locations[5:]],          'Y': [25 - location[1] for location in locations[5:]]          })      off_.plot(x='Y', y='X', style='X', ax=court, alpha=1, label='Offensive players')      def_.plot(x='Y', y='X', style='X', ax=court, alpha=1, label='Defensive players')      label_point(off_, court)      label_point(def_, court)    def label_point(df, ax):      for i, point in df.iterrows():          ax.text(point[df.columns[1]]+ .2, point[df.columns[0]], str(int(i+1)), size=20)    def preprocess(locations):      input = pd.DataFrame(          {              'AtShot_loc_x_off_player_1': [locations[0][0]],              'AtShot_loc_y_off_player_1': [locations[0][1]],              'AtShot_loc_x_off_player_2': [locations[1][0]],              'AtShot_loc_y_off_player_2': [locations[1][1]],              'AtShot_loc_x_off_player_3': [locations[2][0]],              'AtShot_loc_y_off_player_3': [locations[2][1]],              'AtShot_loc_x_off_player_4': [locations[3][0]],              'AtShot_loc_y_off_player_4': [locations[3][1]],              'AtShot_loc_x_off_player_5': [locations[4][0]],              'AtShot_loc_y_off_player_5': [locations[4][1]],              'AtShot_loc_x_def_player_1': [locations[5][0]],              'AtShot_loc_y_def_player_1': [locations[5][1]],              'AtShot_loc_x_def_player_2': [locations[6][0]],              'AtShot_loc_y_def_player_2': [locations[6][1]],              'AtShot_loc_x_def_player_3': [locations[7][0]],              'AtShot_loc_y_def_player_3': [locations[7][1]],              'AtShot_loc_x_def_player_4': [locations[8][0]],              'AtShot_loc_y_def_player_4': [locations[8][1]],              'AtShot_loc_x_def_player_5': [locations[9][0]],              'AtShot_loc_y_def_player_5': [locations[9][1]]          }      )        input[[col for col in input.columns if '_y_' in col]] = (25 - input[          [col for col in input.columns if '_y_' in col]]) / (25 - (-25))      input[[col for col in input.columns if '_x_' in col]] = (47 - input[          [col for col in input.columns if '_x_' in col]]) / (47 - (-47))      return input      def stack_bar(offensive_proba, defensive_proba):      fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing = 0.15)      probas = ['Offending<br>Team<br>', 'Deffending<br>Team<br>']  # text to show        # Move common code to a function to reuse multiple times:      def plot_bar(probas, visible):          x_list = [offensive_proba, defensive_proba]          for x, proba in zip(x_list, probas):              fig.add_trace(                  go.Bar(x=[x * 100],  # just one number value for a bar                         y=['Probability'],                         name='Probability',                         visible=visible,                         opacity=1,                         orientation='h',                         text=(x),                         textposition='inside',                         texttemplate=proba + '%{text:.1%}'),                  row=1, col=1)        plot_bar(probas, True)      fig.update_layout(barmode='stack')      # Controlling text fontsize with uniformtext      fig.update_layout(showlegend=False)  # hide ledend      # Set axis font:      fig.update_yaxes(tickfont=dict(size=14))      st.plotly_chart(fig, use_container_width=True)      if __name__ == '__main__':      # this is the trained model      LDA = load('LDA.joblib')      st.write('''      # Welcome to Rebound Probability Prediction!      ''')      # placeholder      imageLocation = st.empty()      fig, court_ax = plt.subplots(1, 1, figsize=(11, 11.2))      create_half_court(court_ax,                             three_line='mens',                             paint_alpha=0.4,                             inner_arc=True)      # plot the half court without scatter      imageLocation.pyplot(fig)      st.sidebar.header('Input players\' locations when the ball is shot:')        locations = []      for condition in ['Offensive', 'Defensive']:          for i in range(5):              x, y = slider_setting(condition, i + 1)              locations.append((x, y))        button = st.sidebar.button('Confirm')        if button:          # plot the locations of players on the court          locate(locations, court_ax)          imageLocation.pyplot(fig)            # pre-processing          input = preprocess(locations)            ## predict          # individual          individual_prediction = LDA.predict_proba(input)[0]          # team          offensive_proba = sum(individual_prediction[:5])          defensive_proba = sum(individual_prediction[5:])            ## data vis          # individual          results = pd.DataFrame(              {                  'Players': ['Offensive Player 1', 'Offensive Player 2', 'Offensive Player 3', 'Offensive Player 4',                              'Offensive Player 5',                              'Defensive Player 1', 'Defensive Player 2', 'Defensive Player 3', 'Defensive Player 4',                              'Defensive Player 5'],                  'Probability': individual_prediction              }            )          st.write('## Individual Probability')          cm = sns.light_palette("orange", as_cmap=True)          results = results.sort_values(by=['Probability'], ascending=False)          st.dataframe(results.style.background_gradient(cmap=cm).set_precision(2))            # team          st.write('## Team Probability')          stack_bar(offensive_proba, defensive_proba)            # celebrate          st.balloons()  
https://stackoverflow.com/questions/65546281/save-elements-locations-to-local-file-after-dragging-dropping January 03, 2021 at 11:05AM

没有评论:

发表评论