Skip to content

Instantly share code, notes, and snippets.

@valex
Last active April 1, 2018 12:25
Show Gist options
  • Select an option

  • Save valex/b076ad9d4c75e88328d0c847dcb10eb2 to your computer and use it in GitHub Desktop.

Select an option

Save valex/b076ad9d4c75e88328d0c847dcb10eb2 to your computer and use it in GitHub Desktop.
var qid = location.search;
var PITCH_APP = {
url: 'pitchplot.json',
save_edit_pitch_url: '/save_pitch.php',
eventsData: [],
activeEvent: null,
data: [],
activeIndex: null,
defaultVideoIndex: 0,
activeVideoIndex: null,
autoplay: false,
options: {
edit_pitch_mode: false,
editPitch: {
edit_pitch_module_selector: '#edit-pitch-module',
edit_pitch_form_selector: '#edit_pitch_form',
edit_pitch_btn_selector: '#edit_pitch_btn',
save_edit_pitch_btn_selector: '#save_edit_pitch_btn',
cancel_edit_pitch_btn_selector: '#cancel_edit_pitch_btn',
input_x_selector: '#edit_pitch_x_input',
input_y_selector: '#edit_pitch_y_input',
form_visible: false,
},
selectModule: {
appendToSelector: '#pitch-select-module',
},
chartModule: {
appendToSelector: '#pitch-chart-module',
width: 500,
height: 281,
margins: {
top: 10,
right: 10,
bottom: 20,
left: 10,
xAxis: {
left: 116,
right: 104,
}},
pitchBg: 'pitch-bg.jpg',
pitchBgAlpha: 0.3,
color: null,
defaultBallColor:'#b3b3b3',
ballHoverClass:"hovered",
ballActiveClass:"active",
svg: null,
mainGroup: null,
tooltip: null,
scaleX: null,
scaleY: null,
strikeZone:{
color: '#2ad146',
width: 1
},
heatmap: {
show: false,
instance: null,
},
topVelo: [],
topVeloNum: 5,
markTopVelo: true,
topSpin: [],
topSpinNum: 5,
markTopSpin: true,
},
videoModule: {
videoJs: null,
videoJsCache: null,
ready: false,
cache: true,
appendToSelector: '#pitch-video-module'
},
tableModule:{
appendToSelector: '#pitch-table-module',
rowActiveClass:"active",
tbody: null,
columns: [
{
key: 'pitchSpeed',
label: 'P Velo',
fixed: 1,
},
{
key: 'pitchSpin',
label: 'Spin',
fixed: 0,
},
{
key: 'pitchType',
label: 'Type',
},
{
key: 'pitchResult',
label: 'Result',
},
{
key: 'exitvelo',
label: 'Exit V',
fixed: 1,
},
{
key: 'videos',
label: '',
},
]
},
},
start: function(){
// Set-up the export button
d3.select('#pitch-export').on('click', function(){
PITCH_APP.export();
});
this.activeVideoIndex = this.defaultVideoIndex;
this.loadData();
},
loadData: function(){
var that = this;
d3.json(this.url, function (error, rawData) {
if (error) throw error;
var data = rawData['RECORDS'].map(function (d) {
var videos=[];
var videoSrc;
if(typeof d["angle1"] != 'undefined' && d["angle1"] !== null){
videoSrc = (''+d["angle1"]).trim();
if(videoSrc.length > 0 ){
videos.push({
src: videoSrc
});
}
}
if(typeof d["angle2"] != 'undefined' && d["angle2"] !== null){
videoSrc = (''+d["angle2"]).trim();
if(videoSrc.length > 0){
videos.push({
src: videoSrc
});
}
}
return {
playID: +d["playID"],
pitchSpeed: +d["pitchvelo"],
exitvelo: +d["exitvelo"],
pitchSpin: +d["pspin"],
pitchX: +d["pitchX"],
pitchY: +d["pitchY"],
pitchType: (''+d["ptype"]).trim().toLowerCase(),
pitchResult: (''+d["presult"]).trim().toLowerCase(),
eventID: +d["eventID"],
eventName: (''+d["eventName"]).trim(),
eventDate: moment(d["eventDate"], "YYYY-MM-DD HH:mm:ss"),
videos: videos
}
});
that.eventsData = d3.nest()
.key(function(d) { return d.eventID; })
.entries(data);
that.updateData();
that.initModules();
that.updateModules();
if(true === that.autoplay){
that.playNextVideo();
}
});
},
initModules: function(){
this.initSelectModule();
this.initTableModule();
this.initChart();
this.initAutoplayCheckbox();
this.initEditPitchModule();
},
// when data changes
updateModules: function(){
this.updateTableModule();
this.updateChart();
this.updateEditPitchModule();
},
// when active item changes
animateModules:function(){
this.animateTableModule();
this.updateVideoModule();
this.animateChart();
this.updateEditPitchModule();
},
updateData: function(){
var that = this;
that.data = d3.merge(
this.eventsData.map(function(d){
return d.values.filter(function(dd){
if(that.activeEvent === null)
return true;
return dd.eventID == that.activeEvent;
})
})
);
var top5Velo = that.data.slice(0); // clone array
var top5Spin = that.data.slice(0); // clone array
top5Velo.sort(function(x, y){
return d3.descending(x.pitchSpeed, y.pitchSpeed);
});
top5Spin.sort(function(x, y){
return d3.descending(x.pitchSpin, y.pitchSpin);
});
top5Velo = top5Velo.slice(0, that.options.chartModule.topVeloNum);
top5Spin = top5Spin.slice(0, that.options.chartModule.topSpinNum);
that.options.chartModule.topVelo = top5Velo;
that.options.chartModule.topSpin = top5Spin;
},
ballManuallySelected: function(index){
this.setIndexAndAnimate(index);
},
setIndexAndAnimate: function(index){
this.setActiveIndex(index);
this.animateModules();
},
isActiveIndexSet: function(){
if(this.activeIndex !== null && this.activeIndex !== '')
return true;
return false;
},
setActiveIndex:function(index){
this.activeIndex = index;
},
hasVideo: function(data){
return data.videos.length;
},
// ============================== EDIT PITCH MODULE ==============================
initEditPitchModule: function(){
var that = this;
var options = this.options.editPitch;
var editPitchBtn = d3.select(options.edit_pitch_btn_selector)
.on('click', function(){
that.toggleEditPitchMode();
});
var cancelEditPitchBtn = d3.select(options.cancel_edit_pitch_btn_selector)
.on('click', function(){
that.stopEditPitchMode();
});
var saveEditPitchBtn = d3.select(options.save_edit_pitch_btn_selector)
.on('click', function(){
that.sendPitchSaveRequest();
});
if(this.options.edit_pitch_mode === false){
this.stopEditPitchMode();
}else{
this.startEditPitchMode();
}
},
updateEditPitchModule: function(){
if( ! this.isActiveIndexSet()){
this.resetEditPitchForm();
return;
}
var data = this.data[this.activeIndex];
this.setEditPitchInputs(data['pitchX'], data['pitchY']);
},
resetEditPitchForm: function(){
this.setEditPitchInputs(null, null);
},
setEditPitchInputs: function(x, y){
var xInput = d3.select(this.options.editPitch.input_x_selector);
var yInput = d3.select(this.options.editPitch.input_y_selector);
xInput.attr('value', x);
yInput.attr('value', y);
},
sendPitchSaveRequest: function(){
var that = this;
if( ! this.isActiveIndexSet()){
return;
}
var data = this.data[this.activeIndex];
var http = new XMLHttpRequest();
var url = this.save_edit_pitch_url;
var x = d3.select(this.options.editPitch.input_x_selector).node().value.trim();
var y = d3.select(this.options.editPitch.input_y_selector).node().value.trim();
if(x=='' || y=='')
return;
var params = "playID="+data['playID']+"&x="+x+"&y="+y+"&pw_p_id="+qid;
http.open("POST", url, true);
//Send the proper header information along with the request
http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
http.onreadystatechange = function() {//Call a function when the state changes.
if(http.readyState == 4 && http.status == 200) {
// success
// update data
if(that.isActiveIndexSet()){
that.data[that.activeIndex]['pitchX'] = x;
that.data[that.activeIndex]['pitchY'] = y;
}
}
};
http.send(params);
},
startEditPitchMode: function(){
this.options.edit_pitch_mode = true;
this.showEditPitchForm();
},
stopEditPitchMode: function(){
var that = this;
this.options.edit_pitch_mode = false;
if(this.isActiveIndexSet()){
var data = this.data[this.activeIndex];
var cx = that.ballXY(data).x;
var cy = that.ballXY(data).y;
that.setCxCyForActiveCircle(
cx,
cy
);
}
this.hideEditPitchForm();
},
toggleEditPitchMode: function(){
if(this.options.edit_pitch_mode === true){
this.stopEditPitchMode();
}else{
this.startEditPitchMode();
}
},
showEditPitchForm: function(){
var options = this.options.editPitch;
var editPitchForm = d3.select(options.edit_pitch_form_selector)
.style('display', 'block');
options.form_visible = true;
},
hideEditPitchForm: function(){
var options = this.options.editPitch;
var editPitchForm = d3.select(options.edit_pitch_form_selector)
.style('display', 'none');
options.form_visible = false;
},
// ============================== VIDEO MODULE ==============================
initVideoPlayer: function(){
var that = this;
var options = this.options.videoModule;
var outerBlock = d3.select(options.appendToSelector).html("");
var innerBlock = outerBlock
.append('div')
.attr('class', 'embed-responsive embed-responsive-16by9');
var videoTag = innerBlock.append('video')
.attr('class', 'pitch-video-js video-js embed-responsive-item');
var videoUnsupportedWarn = videoTag.append('p')
.attr('class', 'vjs-no-js')
.html('To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a>');
options.videoJs = videojs(document.querySelector('.pitch-video-js'), {
aspectRatio: '16:9',
controls: true,
autoplay: false,
preload: 'auto',
});
options.videoJs.on('playing', function() {
});
options.videoJs.on('ended', function() {
if(true == that.autoplay){
that.playNextVideo();
}
});
options.videoJs.ready(function() {
that.onVideoPlayerReady();
});
if(options.cache === true){
var cacheInnerBlock = outerBlock
.append('div')
.style('width', '1px')
.style('height', '1px')
.attr('class', 'embed-responsive embed-responsive-16by9');
var cacheVideoTag = cacheInnerBlock.append('video')
.attr('class', 'video-js video-js-cache embed-responsive-item');
options.videoJsCache = videojs(document.querySelector('.video-js-cache'), {
aspectRatio: '16:9',
controls: true,
autoplay: false,
muted: true,
preload: 'auto',
});
var nextVideo = this.getNextVideoIndex();
var videoSrc = this.data[nextVideo.row]['videos'][nextVideo.index]['src'];
options.videoJsCache.src({
src: videoSrc,
type: 'video/mp4'
});
options.videoJsCache.on('loadedmetadata', function() {
var video = this.children_[0];
if (video.buffered.length === 0) return;
var bufferedSeconds = video.buffered.end(0) - video.buffered.start(0);
console.log(bufferedSeconds + ' seconds of video are ready to play!');
});
}
},
onVideoPlayerReady: function(){
this.options.videoModule.ready = true;
this.options.videoModule.videoJs.play();
},
updateVideoModule: function(){
var options = this.options.videoModule;
this.disposeVideoPlayer();
if(this.isActiveIndexSet()){
var datum = this.data[this.activeIndex];
if(this.hasVideo(datum)){
this.initVideoPlayer();
if(typeof datum['videos'][this.activeVideoIndex] == 'undefined')
this.setVideoActiveIndex(this.defaultVideoIndex);
this.setVideoSrc(datum['videos'][this.activeVideoIndex]['src']);
}
this.highlightVideoIcon();
}
},
setVideoSrc: function(videoSrc){
this.options.videoModule.videoJs.src({
src: videoSrc,
type: 'video/mp4'
});
},
playNextVideo: function(){
if( ! this.datasetHasVideo())
return;
var currentVideoIndex = this.getCurrentVideoIndex();
var nextVideoIndex = this.getNextVideoIndex();
this.setVideoActiveIndex(nextVideoIndex.index);
if(currentVideoIndex.row === nextVideoIndex.row){
this.updateVideoModule();
}else{
this.setIndexAndAnimate(nextVideoIndex.row)
}
},
datasetHasVideo: function(){
for(var i=0; i < this.data.length; i++){
if(this.hasVideo(this.data[i]))
return true;
}
return false;
},
getCurrentVideoIndex: function(){
return {
row: this.activeIndex,
index: this.activeVideoIndex,
};
},
getNextVideoIndex: function(){
var nextVideo = this.getCurrentVideoIndex();
do{
if(nextVideo.row === null || nextVideo.index === null){
nextVideo = {
row: 0,
index: this.defaultVideoIndex,
};
}else{
nextVideo['index']++;
}
if(typeof this.data[nextVideo['row']]['videos'][nextVideo['index']] === 'undefined'){
nextVideo['index'] = this.defaultVideoIndex;
nextVideo['row']++;
if(typeof this.data[nextVideo['row']] === 'undefined'){
nextVideo['row'] = 0;
}
}
}while(typeof this.data[nextVideo['row']]['videos'][nextVideo['index']] === 'undefined')
return nextVideo;
},
startAutoplay: function(){
if(this.autoplay === true)
return;
this.autoplay = true;
var autoplayCheckbox = d3.select("#pitch-autoplay-checkbox");
autoplayCheckbox.node().checked = this.autoplay;
this.playNextVideo();
},
stopAutoplay: function(){
if(this.autoplay === false)
return;
this.autoplay = false;
var autoplayCheckbox = d3.select("#pitch-autoplay-checkbox");
autoplayCheckbox.node().checked = this.autoplay;
},
initAutoplayCheckbox: function(){
var that = this;
var autoplayCheckbox = d3.select("#pitch-autoplay-checkbox");
if(autoplayCheckbox.size() <= 0)
return;
autoplayCheckbox.node().checked = that.autoplay;
autoplayCheckbox.on("change", function(){
!!this.checked ? that.startAutoplay() : that.stopAutoplay();
});
},
disposeVideoPlayer: function(){
if(this.options.videoModule.videoJs !== null){
try{
this.options.videoModule.videoJs.dispose();
}catch (e){
console.log(e);
}
this.options.videoModule.videoJs = null;
this.options.videoModule.ready = false;
}
if(this.options.videoModule.cache === true && this.options.videoModule.videoJsCache !== null){
try{
this.options.videoModule.videoJsCache.dispose();
}catch (e){
console.log(e);
}
this.options.videoModule.videoJsCache = null;
}
},
// ============================== TABLE MODULE ==============================
initTableModule: function() {
var options = this.options.tableModule;
var table = d3.select(options.appendToSelector).append('table')
.attr('class', 'table');
var thead = table.append('thead');
options.tbody = table.append('tbody');
// append the header row
thead.append('tr')
.selectAll('th')
.data(options.columns)
.enter()
.append('th')
.text(function (column) { return column.label; });
},
updateTableModule:function(){
var that = this;
var options = this.options.tableModule;
// create a row for each object in the data
var rowsSelection = options.tbody.selectAll('tr')
.data(this.data);
rowsSelection.exit().remove();
var rowsEnter = rowsSelection
.enter()
.append('tr')
.on('click', function(d, i){
PITCH_APP.ballManuallySelected(i);
});
var rows = rowsSelection.merge(rowsEnter);
// create a cell in each row for each column
var cellsSelection = rows.selectAll('td')
.data(function (row, i) {
return options.columns.map(function (column) {
var value, type;
switch(column.key){
case 'videos':
type = 'videos';
value = row.videos;
break;
case 'pitchType':
type = 'string';
switch(row[column.key]){
case 'fastball':
value = 'FB';
break;
case 'curveball':
value = 'CB';
break;
case 'slider':
value = 'SL';
break;
case 'changeup':
value = 'CH';
break;
case 'cutter':
value = 'CUT';
break;
case 'splitter':
value = 'SPL';
break;
case 'knuckleball':
value = 'KN';
break;
case 'screwball':
value = 'SCR';
break;
case 'forkbal':
value = 'FK';
break;
default:
value = row[column.key];
break;
}
break;
case 'pitchResult':
type = 'string';
value = row[column.key];
break;
default:
type = 'number';
value = parseFloat(row[column.key]).toFixed(column.fixed);
if(value <= 0)
value = '';
break;
}
return {
type: type,
value: value
};
});
});
var cellsEnter = cellsSelection
.enter()
.append('td');
var cells = cellsSelection.merge(cellsEnter);
cells.each(function (d) {
var el = d3.select(this);
switch(d.type){
case 'videos':
el.html('');
el.append('div')
.attr('class', 'text-nowrap')
.selectAll('i')
.data(d.value)
.enter()
.append('i')
.attr('class', function(d,i){
if(i > 0){
return 'fa fa-video-camera fa-rotate-315';
}else{
return 'fa fa-video-camera';
}
})
.style('margin-right', '6px')
.on('click', function(d, video_i){
that.onClickVideoIcon(video_i);
});
break;
case 'string':
case 'number':
el.html(function (d) { return d.value; });
break;
}
});
this.clearAllActiveRows();
},
animateTableModule: function(){
this.setRowActive(this.activeIndex);
},
onClickVideoIcon: function(video_index){
this.setVideoActiveIndex(video_index);
},
setVideoActiveIndex: function(index){
this.activeVideoIndex = index;
},
setRowActive: function(index){
var options = this.options.tableModule;
// only one active
this.clearAllActiveRows();
var tr = options.tbody.selectAll('tr').filter(function(d,i){return i === index});
if(tr.size() > 0){
var alreadyIsActive = tr.classed(options.rowActiveClass);
if( ! alreadyIsActive){
tr.classed(options.rowActiveClass, ! alreadyIsActive);
}
}
},
clearAllActiveRows: function(){
var options = this.options.tableModule;
options.tbody.selectAll('tr')
.classed(options.rowActiveClass, false);
},
highlightVideoIcon: function(){
var videoOptions = this.options.videoModule;
this.extinguishAllVideoIcons();
d3.select('#pitch-table-module tr.active .fa-video-camera:nth-child('+(1+this.activeVideoIndex)+')').classed('green', true);
},
extinguishAllVideoIcons: function(){
d3.selectAll('#pitch-table-module .fa-video-camera').classed('green', false);
},
// ============================== CHART MODULE ==============================
initChart: function(){
var that = this;
var options = this.options.chartModule;
var chartWidth = options.width - options.margins.left - options.margins.right,
chartHeight = options.height - options.margins.top - options.margins.bottom;
options.svg = d3.select(options.appendToSelector)
.append("svg")
.attr("width", options.width)
.attr("height", options.height)
.on('click', function() {
var coords = d3.mouse(this);
if(false === that.options.edit_pitch_mode || ! that.isActiveIndexSet())
return;
var cx = coords[0] - that.options.chartModule.margins.left;
var cy = coords[1] - that.options.chartModule.margins.top;
that.setCxCyForActiveCircle(
cx,
cy
);
// set inputs
that.setEditPitchInputs(
that.options.chartModule.scaleX.invert(cx-that.options.chartModule.margins.xAxis.left).toFixed(3),
that.options.chartModule.scaleY.invert(cy).toFixed(3)
);
});
// background image
options.svg.append("svg:defs")
.append("svg:pattern")
.attr("id", "pitch-bg")
.attr('patternUnits', 'userSpaceOnUse')
.attr("x", 0)
.attr("y", 0)
.attr('width', options.width)
.attr('height', options.height)
.append("svg:image")
.attr("xlink:href", options.pitchBg)
.attr("x", 0)
.attr("y", 0)
.attr('width', options.width)
.attr('height', options.height);
options.svg.append("rect")
.attr('id', 'pitch-bg-rect')
.attr('fill', 'url(#pitch-bg)')
.attr('width', options.width)
.attr('height', options.height);
options.mainGroup = options.svg.append('g')
.attr('transform', 'translate(' + options.margins.left + ',' + options.margins.top + ')')
.attr('id', options.id);
options.scaleX = d3.scaleLinear()
.domain([-4, 4])
.range([0, chartWidth - options.margins.xAxis.left - options.margins.xAxis.right]);
options.scaleY = d3.scaleLinear()
.domain([0, 6])
.range([chartHeight, 0]);
options.color = d3.scaleOrdinal()
.domain(["fastball", "curveball", "changeup", "slider", "knuckleball", "splitter"])
.range(["red", "blue", "yellow", "green", "purple", "orange", "#b3b3b3"]);
this.initHeatmapCheckbox();
this.initMarkTopVeloCheckbox();
this.initMarkTopSpinCheckbox();
// tooltip
options.tooltip = d3.select("body")
.append("div")
.attr("class", "pitch-tooltip")
.style("opacity", 0);
// strikeZone
var strikeZone = options.svg.append('g')
.attr('transform', 'translate(' + (options.scaleX(-1.1) + options.margins.left + options.margins.xAxis.left) + ',' + (options.scaleY(3.5) + options.margins.top) + ')');
strikeZone.append('rect')
.attr('width', options.scaleX(1.1) - options.scaleX(-1.1))
.attr('height', options.scaleY(1.0) - options.scaleY(3.5))
.attr('fill', 'none')
.attr('stroke', options.strikeZone.color)
.attr('stroke-width', options.strikeZone.width);
// axes
var axisXGroup = options.svg.append('g')
.attr('transform', 'translate(' + (options.margins.left + options.margins.xAxis.left) + ',' + (options.height - options.margins.bottom) + ')');
var axisX = d3.axisBottom(options.scaleX);
axisXGroup.call(axisX);
var axisYGroup = options.svg.append('g')
.attr('transform', 'translate(' + (options.margins.left + options.margins.xAxis.left + options.scaleX(0)) + ',' + options.margins.top + ')');
var axisY = d3.axisLeft(options.scaleY).ticks(6);
axisYGroup.call(axisY);
this.styleAxis();
// labels
var labels = [
{
color: options.color('fastball'),
label: 'Fastball'
},
{
color: options.color('curveball'),
label: 'Curveball'
},
{
color: options.color('changeup'),
label: 'Changeup'
},
{
color: options.color('slider'),
label: 'Slider'
},
{
color: options.color('knuckleball'),
label: 'Knuckleball'
},
{
color: options.color('splitter'),
label: 'Splitter'
},
]
var legends = options.mainGroup.selectAll('rect.legend')
.data(labels);
legends
.enter()
.append('rect')
.attr('class', 'legend')
.attr('width', 10)
.attr('height', 10)
.attr('fill', function(d){return d.color;})
.attr('transform', function(d,i) {return 'translate(' + options.margins.left + ',' + (options.margins.top + i*15) + ')'})
legends
.enter()
.append('text')
.text(function(d){return d.label;})
.attr('fill', 'white')
.attr('font-size', '12px')
.attr('x', 13 + options.margins.left)
.attr('y', function(d,i){return options.margins.top + 9+i*15})
},
initHeatmap: function(){
var that = this;
var options = this.options.chartModule;
var heatmapWidth = options.width,
heatmapHeight = options.height;
var heatmapWrapper = d3.select(options.appendToSelector)
.append("div")
.attr("class", 'pitch-heatmap-wrapper')
.style("width", heatmapWidth+"px")
.style("height", heatmapHeight+"px");
var heatmap = heatmapWrapper
.append('div')
.attr("class", 'pitch-heatmap');
// minimal heatmap instance configuration
options.heatmap.instance = h337.create({
// only container is required, the rest will be defaults
container: document.querySelector('.pitch-heatmap'),
});
},
initHeatmapCheckbox: function(){
var that = this;
var heatmapCheckbox = d3.select("#pitch-heatmap-checkbox");
heatmapCheckbox.on("change", function(){
that.options.chartModule.heatmap.show = !!this.checked;
that.onHeatmapCheckboxChange();
});
},
onHeatmapCheckboxChange: function(){
this.drawBackground();
},
drawBackground: function () {
var that = this;
var options = this.options.chartModule;
if(true === options.heatmap.show){
that.drawHeatmap();
d3.select('#pitch-bg-rect').attr('opacity', options.pitchBgAlpha);
}else{
that.clearHeatmap();
d3.select('#pitch-bg-rect').attr('opacity', 1.0);
}
},
setCxCyForActiveCircle: function(cx, cy){
var that = this;
var options = this.options.chartModule;
var circle = options.svg.selectAll('path.pitch-ball')
.filter(function (d, i) {
return i === that.activeIndex;
});
if(circle.size() <= 0)
return;
circle.attr('transform', function(d, i){
return 'translate(' + (cx) + ','+ (cy) +')';
});
},
drawHeatmap: function(){
var that = this;
var options = this.options.chartModule;
// now generate some random data
var points = [];
var max = 1;
if(options.heatmap.instance === null){
this.initHeatmap();
}
for(var i=0; i < that.data.length; i++){
var coordinates = that.ballXY(that.data[i]);
var point = {
x: Math.round(coordinates.x + options.margins.left),
y: Math.round(coordinates.y + options.margins.top),
value: 0.7
};
points.push(point);
}
// heatmap data format
var data = {
max: max,
data: points
};
// if you have a set of datapoints always use setData instead of addData
// for data initialization
options.heatmap.instance.setData(data);
},
clearHeatmap: function(){
var that = this;
var options = this.options.chartModule;
if(options.heatmap.instance === null)
return;
options.heatmap.instance.setData({
max: 0,
min: 0,
data: [
]
});
},
initMarkTopVeloCheckbox: function(){
var that = this;
var el = d3.select("#markTopVelo");
if(el.size() <= 0)
return;
el.node().checked = that.options.chartModule.markTopVelo;
el.on("change", function(){
that.options.chartModule.markTopVelo = !!this.checked;
that.updateChart();
});
},
initMarkTopSpinCheckbox: function(){
var that = this;
var el = d3.select("#markTopSpin");
if(el.size() <= 0)
return;
el.node().checked = that.options.chartModule.markTopSpin;
el.on("change", function(){
that.options.chartModule.markTopSpin = !!this.checked;
that.updateChart();
});
},
isInTopVelo: function(d){
for(var i = 0; i < this.options.chartModule.topVelo.length; i++){
if(this.options.chartModule.topVelo[i]['playID'] === d['playID'])
return true;
}
return false;
},
isInTopSpin: function(d){
for(var i = 0; i < this.options.chartModule.topSpin.length; i++){
if(this.options.chartModule.topSpin[i]['playID'] === d['playID'])
return true;
}
return false;
},
updateChart: function(){
var that = this;
var options = this.options.chartModule;
var symbol = d3.symbol().size(50);
var circlesSelection = options.mainGroup.selectAll('path.pitch-ball')
.data(this.data);
circlesSelection.exit().remove();
var circlesEnter = circlesSelection
.enter()
.append('path')
.attr('class', 'pitch-ball')
.on('click', function(d, i) {
var el = d3.select(this);
if(true === that.options.edit_pitch_mode && that.isActiveIndexSet())
return;
that.ballManuallySelected(i);
})
.on('mouseenter', function(d) {
var el = d3.select(this);
// tooltip
options.tooltip.transition()
.duration(200)
.style("opacity", .8);
options.tooltip.html(parseFloat(d.pitchSpeed).toFixed(1)+' mph')
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 28 - 5) + "px");
// classes
el.classed(options.ballHoverClass, true);
})
.on('mouseout', function(d) {
var el = d3.select(this);
// tooltip
options.tooltip.transition()
.duration(500)
.style("opacity", 0);
// classes
el.classed(options.ballHoverClass, false);
});
var circles = circlesSelection.merge(circlesEnter);
circles
.attr("d", symbol.type(function (d) {
if(options.markTopVelo && that.isInTopVelo(d)){
return d3.symbolStar;
}
if(options.markTopSpin && that.isInTopSpin(d)){
return d3.symbolStar;
}
return d3.symbolCircle;
}))
.attr('fill', function(d,i){return options.color(d.pitchType)})
.attr('transform', function(d, i){
var coords = that.ballXY(d);
return 'translate(' + (coords.x) + ','+ (coords.y) +')';
});
this.clearAllActiveCircles();
this.drawBackground();
},
animateChart: function(){
var that = this;
var options = this.options.chartModule;
this.clearAllActiveCircles();
var el = options.mainGroup.selectAll('path.pitch-ball').filter(function(d,i){return i===that.activeIndex});
if(el.size() <= 0)
return;
// only one active
var alreadyIsActive = el.classed(options.ballActiveClass);
if( ! alreadyIsActive){
el.classed(options.ballActiveClass, ! alreadyIsActive);
el.classed(options.ballHoverClass, false);
}
},
clearAllActiveCircles: function(){
var options = this.options.chartModule;
options.mainGroup.selectAll('path.pitch-ball')
.classed(options.ballActiveClass, false);
},
styleAxis: function(){
var that = this;
var options = this.options.chartModule;
options.svg.selectAll('path.domain').attr('stroke', '#DEDEDE');
options.svg.selectAll('g.tick line').attr('stroke', '#DEDEDE');
options.svg.selectAll('g.tick text').attr('fill', '#DEDEDE');
},
ballXY: function(d){
var that = this;
var options = this.options.chartModule;
return {
x: options.margins.xAxis.left + options.scaleX(d.pitchX),
y: options.scaleY(d.pitchY),
}
},
// ============================== SELECT MODULE ==============================
initSelectModule:function(){
var options = this.options.selectModule;
var select = d3.select(options.appendToSelector).append('select')
.attr('name', 'active_event')
.attr('class', 'form-control');
var nullOption = select.append('option')
.attr('value', '')
.text('ALL PITCHES');
for(var i=0; i < this.eventsData.length; i++){
select.append('option')
.attr('value', this.eventsData[i].values[0].eventID)
.text(this.eventsData[i].values[0].eventName);
}
select.on('change', this.onSelectChange);
},
onSelectChange: function (e) {
var old = PITCH_APP.activeEvent;
PITCH_APP.activeEvent = this.value.length > 0 ? this.value : null;
if(old != PITCH_APP.activeEvent){
PITCH_APP.onActiveEventChange();
}
},
onActiveEventChange: function(){
this.setActiveIndex(null);
this.setVideoActiveIndex(this.defaultVideoIndex);
this.updateData();
this.updateModules();
this.updateVideoModule();
if(true === this.autoplay){
this.playNextVideo();
}
},
// ============================== EXPORT MODULE ==============================
export: function(){
var svgString = this.getSVGString(this.options.chartModule.svg.node());
this.svgString2Image( svgString, 2 * this.options.chartModule.width, 2 * this.options.chartModule.height, 'png', save ); // passes Blob and filesize String to the callback
function save( dataBlob, filesize ){
saveAs( dataBlob, 'pitches_chart.png' ); // FileSaver.js function
}
},
// Below are the functions that handle actual exporting:
// getSVGString ( svgNode ) and svgString2Image( svgString, width, height, format, callback )
getSVGString: function( svgNode ) {
svgNode.setAttribute('xlink', 'http://www.w3.org/1999/xlink');
var cssStyleText = getCSSStyles( svgNode );
appendCSS( cssStyleText, svgNode );
var serializer = new XMLSerializer();
var svgString = serializer.serializeToString(svgNode);
svgString = svgString.replace(/(\w+)?:?xlink=/g, 'xmlns:xlink='); // Fix root xlink without namespace
svgString = svgString.replace(/NS\d+:href/g, 'xlink:href'); // Safari NS namespace fix
return svgString;
function getCSSStyles( parentElement ) {
var selectorTextArr = [];
// Add Parent element Id and Classes to the list
selectorTextArr.push( '#'+parentElement.id );
for (var c = 0; c < parentElement.classList.length; c++)
if ( !contains('.'+parentElement.classList[c], selectorTextArr) )
selectorTextArr.push( '.'+parentElement.classList[c] );
// Add Children element Ids and Classes to the list
var nodes = parentElement.getElementsByTagName("*");
for (var i = 0; i < nodes.length; i++) {
var id = nodes[i].id;
if ( !contains('#'+id, selectorTextArr) )
selectorTextArr.push( '#'+id );
var classes = nodes[i].classList;
for (var c = 0; c < classes.length; c++)
if ( !contains('.'+classes[c], selectorTextArr) )
selectorTextArr.push( '.'+classes[c] );
}
// Extract CSS Rules
var extractedCSSText = "";
for (var i = 0; i < document.styleSheets.length; i++) {
var s = document.styleSheets[i];
try {
if(!s.cssRules) continue;
} catch( e ) {
if(e.name !== 'SecurityError') throw e; // for Firefox
continue;
}
var cssRules = s.cssRules;
for (var r = 0; r < cssRules.length; r++) {
if ( contains( cssRules[r].selectorText, selectorTextArr ) )
extractedCSSText += cssRules[r].cssText;
}
}
return extractedCSSText;
function contains(str,arr) {
return arr.indexOf( str ) === -1 ? false : true;
}
}
function appendCSS( cssText, element ) {
var styleElement = document.createElement("style");
styleElement.setAttribute("type","text/css");
styleElement.innerHTML = cssText;
var refNode = element.hasChildNodes() ? element.children[0] : null;
element.insertBefore( styleElement, refNode );
}
},
svgString2Image: function( svgString, width, height, format, callback ) {
var that = this;
var format = format ? format : 'png';
var imgsrc = 'data:image/svg+xml;base64,'+ btoa( unescape( encodeURIComponent( svgString ) ) ); // Convert SVG string to data URL
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
var image = new Image();
image.onload = function() {
context.clearRect ( 0, 0, width, height );
var backgroundImage = new Image();
backgroundImage.onload = function(){
if(that.options.chartModule.heatmap.show){
var heatmapDataURL = that.options.chartModule.heatmap.instance.getDataURL();
var heatmap = new Image();
heatmap.onload = function(){
context.globalAlpha = that.options.chartModule.pitchBgAlpha;
context.drawImage(backgroundImage, 0, 0, width, height);
context.globalAlpha = 1.0;
context.drawImage(heatmap, 0, 0, width, height);
context.drawImage(image, 0, 0, width, height);
that.onAllImagesLoad(canvas, callback);
};
heatmap.src = heatmapDataURL;
}else{
context.drawImage(backgroundImage, 0, 0, width, height);
context.drawImage(image, 0, 0, width, height);
//
that.onAllImagesLoad(canvas, callback);
}
};
backgroundImage.src = that.options.chartModule.pitchBg;
};
image.src = imgsrc;
},
onAllImagesLoad: function(canvas, callback){
canvas.toBlob( function(blob) {
var filesize = Math.round( blob.length/1024 ) + ' KB';
if ( callback ) callback( blob, filesize );
});
}
};
PITCH_APP.start();
<!DOCTYPE html>
<meta charset='utf-8'>
<html>
<head>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/video.js/6.6.0/alt/video-js-cdn.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link rel='stylesheet' href='style.css'>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-sm-4" id="pitch-select-module"></div>
<div class="col-sm-4">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="pitch-heatmap-checkbox">
<label class="form-check-label" for="pitch-heatmap-checkbox">Heatmap</label>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div id="pitch-chart-module"></div>
<div>
<button type="button" id="pitch-export" class="btn btn-outline-primary btn-sm">Export to PNG</button>
<label class="switch" title="AUTOPLAY">
<input id="pitch-autoplay-checkbox" type="checkbox">
<span class="slider round"></span>
</label>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="markTopVelo">
<label class="form-check-label" for="markTopVelo">
Mark Top Velo
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="markTopSpin">
<label class="form-check-label" for="markTopSpin">
Mark Top Spin
</label>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="row">
<div class="col-sm-12" id="edit-pitch-module">
<div class="row">
<div class="col-sm-2">
<button type="button" id="edit_pitch_btn" class="btn btn-primary">Edit Pitch</button>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<form id="edit_pitch_form" class="form-inline my-2">
<input readonly id="edit_pitch_y_input" type="text" class="form-control mr-sm-2" placeholder="Y (Height)">
<input readonly id="edit_pitch_x_input" type="text" class="form-control mr-sm-2" placeholder="X (Width)">
<button type="button" id="save_edit_pitch_btn" class="btn btn-success mr-sm-2">Save</button>
<button type="button" id="cancel_edit_pitch_btn" class="btn btn-danger mr-sm-2">Cancel</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-4" id="pitch-table-module"></div>
<div class="col-sm-8" id="pitch-video-module"></div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/video.js/6.6.3/video.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/heatmap.js/2.0.2/heatmap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/javascript-canvas-to-blob/3.14.0/js/canvas-to-blob.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.3/FileSaver.min.js"></script>
<script type='text/javascript' src='charts_pitcher_plot.js'></script>
</body>
</html>
{
"RECORDS": [
{
"playID": "15007",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Swinging Strike",
"pitchvelo": "71.658",
"pitchY": "3.000",
"pitchX": "-0.083",
"ptype": "Changeup",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/10_JosephPreusser_MatthewGoodheart.mp4",
"video_id": "21049",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/10_JosephPreusser_MatthewGoodheart.mp4",
"pspin": "1637",
"exitvelo": "0.000"
},
{
"playID": "15008",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "71.845",
"pitchY": "1.417",
"pitchX": "-2.583",
"ptype": "Changeup",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/11_JosephPreusser_MatthewGoodheart.mp4",
"video_id": "21051",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/11_JosephPreusser_MatthewGoodheart.mp4",
"pspin": "1621",
"exitvelo": "0.000"
},
{
"playID": "15009",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Foul Ball",
"pitchvelo": "68.864",
"pitchY": "3.333",
"pitchX": "-0.250",
"ptype": "Changeup",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/12_JosephPreusser_MatthewGoodheart.mp4",
"video_id": "21053",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/12_JosephPreusser_MatthewGoodheart.mp4",
"pspin": "1659",
"exitvelo": "0.000"
},
{
"playID": "15010",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Single",
"pitchvelo": "70.361",
"pitchY": "2.583",
"pitchX": "-0.083",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/13_JosephPreusser_MatthewGoodheart.mp4",
"video_id": "21055",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/13_JosephPreusser_MatthewGoodheart.mp4",
"pspin": "1735",
"exitvelo": "91.392"
},
{
"playID": "15011",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Single",
"pitchvelo": "71.593",
"pitchY": "2.667",
"pitchX": "1.083",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/14_JosephPreusser_AndresSosa.mp4",
"video_id": "21057",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/14_JosephPreusser_AndresSosa.mp4",
"pspin": "1610",
"exitvelo": "0.000"
},
{
"playID": "15012",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Error",
"pitchvelo": "72.970",
"pitchY": "2.250",
"pitchX": "-0.417",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/15_JosephPreusser_AlerickSolearie.mp4",
"video_id": "21059",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/15_JosephPreusser_AlerickSolearie.mp4",
"pspin": "1703",
"exitvelo": "0.000"
},
{
"playID": "15013",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Called Strike",
"pitchvelo": "72.133",
"pitchY": "2.333",
"pitchX": "-0.167",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/16_JosephPreusser_SergioMacias.mp4",
"video_id": "21061",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/16_JosephPreusser_SergioMacias.mp4",
"pspin": "1696",
"exitvelo": "0.000"
},
{
"playID": "15014",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Contact Out",
"pitchvelo": "72.455",
"pitchY": "2.417",
"pitchX": "-0.417",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/17_JosephPreusser_SergioMacias.mp4",
"video_id": "21063",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/17_JosephPreusser_SergioMacias.mp4",
"pspin": "0",
"exitvelo": "95.483"
},
{
"playID": "15015",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "71.162",
"pitchY": "3.083",
"pitchX": "2.167",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/18_JosephPreusser_JakeMatheny.mp4",
"video_id": "21065",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/18_JosephPreusser_JakeMatheny.mp4",
"pspin": "1642",
"exitvelo": "0.000"
},
{
"playID": "15016",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Foul Ball",
"pitchvelo": "69.958",
"pitchY": "2.500",
"pitchX": "1.083",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/19_JosephPreusser_JakeMatheny.mp4",
"video_id": "21067",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/19_JosephPreusser_JakeMatheny.mp4",
"pspin": "1690",
"exitvelo": "0.000"
},
{
"playID": "15017",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "69.750",
"pitchY": "2.417",
"pitchX": "3.417",
"ptype": "Changeup",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/20_JosephPreusser_JakeMatheny.mp4",
"video_id": "21069",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/20_JosephPreusser_JakeMatheny.mp4",
"pspin": "1142",
"exitvelo": "0.000"
},
{
"playID": "15018",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Swinging Strike",
"pitchvelo": "71.706",
"pitchY": "3.333",
"pitchX": "-0.833",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/21_JosephPreusser_JakeMatheny.mp4",
"video_id": "21071",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/21_JosephPreusser_JakeMatheny.mp4",
"pspin": "2157",
"exitvelo": "0.000"
},
{
"playID": "15019",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Foul Ball",
"pitchvelo": "70.151",
"pitchY": "2.667",
"pitchX": "1.333",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/22_JosephPreusser_JakeMatheny.mp4",
"video_id": "21073",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/22_JosephPreusser_JakeMatheny.mp4",
"pspin": "1412",
"exitvelo": "0.000"
},
{
"playID": "15020",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Contact Out",
"pitchvelo": "71.520",
"pitchY": "2.667",
"pitchX": "-0.333",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/23_JosephPreusser_JakeMatheny.mp4",
"video_id": "21075",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/23_JosephPreusser_JakeMatheny.mp4",
"pspin": "1714",
"exitvelo": "84.597"
},
{
"playID": "15021",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Called Strike",
"pitchvelo": "70.224",
"pitchY": "2.833",
"pitchX": "-0.167",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/24_JosephPreusser_MarioMoralez.mp4",
"video_id": "21077",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/24_JosephPreusser_MarioMoralez.mp4",
"pspin": "1658",
"exitvelo": "0.000"
},
{
"playID": "15022",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Contact Out",
"pitchvelo": "71.378",
"pitchY": "2.083",
"pitchX": "-0.250",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/25_JosephPreusser_MarioMoralez.mp4",
"video_id": "21079",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/25_JosephPreusser_MarioMoralez.mp4",
"pspin": "1679",
"exitvelo": "82.177"
},
{
"playID": "15038",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "66.904",
"pitchY": "2.250",
"pitchX": "-1.250",
"ptype": "Slider",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/41_JosephPreusser_HerbertIser.mp4",
"video_id": "21111",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/41_JosephPreusser_HerbertIser.mp4",
"pspin": "1680",
"exitvelo": "0.000"
},
{
"playID": "15039",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Foul Ball",
"pitchvelo": "71.056",
"pitchY": "2.500",
"pitchX": "-0.333",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/42_JosephPreusser_HerbertIser.mp4",
"video_id": "21113",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/42_JosephPreusser_HerbertIser.mp4",
"pspin": "1662",
"exitvelo": "57.024"
},
{
"playID": "15040",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Single",
"pitchvelo": "70.546",
"pitchY": "3.167",
"pitchX": "-0.500",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/43_JosephPreusser_HerbertIser.mp4",
"video_id": "21115",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/43_JosephPreusser_HerbertIser.mp4",
"pspin": "1691",
"exitvelo": "98.021"
},
{
"playID": "15041",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "71.091",
"pitchY": "0.583",
"pitchX": "-1.250",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/44_JosephPreusser_TyrieceSilas.mp4",
"video_id": "21117",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/44_JosephPreusser_TyrieceSilas.mp4",
"pspin": "1704",
"exitvelo": "0.000"
},
{
"playID": "15042",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "71.751",
"pitchY": "2.083",
"pitchX": "1.500",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/45_JosephPreusser_TyrieceSilas.mp4",
"video_id": "21119",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/45_JosephPreusser_TyrieceSilas.mp4",
"pspin": "1635",
"exitvelo": "0.000"
},
{
"playID": "15043",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Single",
"pitchvelo": "71.194",
"pitchY": "1.167",
"pitchX": "-1.667",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/46_JosephPreusser_TyrieceSilas.mp4",
"video_id": "21121",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/46_JosephPreusser_TyrieceSilas.mp4",
"pspin": "1674",
"exitvelo": "0.000"
},
{
"playID": "15044",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "68.711",
"pitchY": "2.750",
"pitchX": "2.000",
"ptype": "Changeup",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/47_JosephPreusser_MattWiseman.mp4",
"video_id": "21123",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/47_JosephPreusser_MattWiseman.mp4",
"pspin": "1145",
"exitvelo": "0.000"
},
{
"playID": "15045",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "69.776",
"pitchY": "0.000",
"pitchX": "-0.000",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/48_JosephPreusser_MattWiseman.mp4",
"video_id": "21125",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/48_JosephPreusser_MattWiseman.mp4",
"pspin": "1738",
"exitvelo": "0.000"
},
{
"playID": "15046",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Contact Out",
"pitchvelo": "72.569",
"pitchY": "2.250",
"pitchX": "-0.083",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/49_JosephPreusser_MattWiseman.mp4",
"video_id": "21127",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/49_JosephPreusser_MattWiseman.mp4",
"pspin": "1715",
"exitvelo": "0.000"
},
{
"playID": "15047",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "73.658",
"pitchY": "1.833",
"pitchX": "-2.167",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/50_JosephPreusser_MatthewGoodheart.mp4",
"video_id": "21129",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/50_JosephPreusser_MatthewGoodheart.mp4",
"pspin": "1735",
"exitvelo": "0.000"
},
{
"playID": "15048",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Double",
"pitchvelo": "69.245",
"pitchY": "2.250",
"pitchX": "-0.167",
"ptype": "Changeup",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/51_JosephPreusser_MatthewGoodheart.mp4",
"video_id": "21131",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/51_JosephPreusser_MatthewGoodheart.mp4",
"pspin": "1729",
"exitvelo": "82.293"
},
{
"playID": "15049",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "73.258",
"pitchY": "2.333",
"pitchX": "1.667",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/52_JosephPreusser_AndresSosa.mp4",
"video_id": "21133",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/52_JosephPreusser_AndresSosa.mp4",
"pspin": "1603",
"exitvelo": "0.000"
},
{
"playID": "15050",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Home Run",
"pitchvelo": "73.407",
"pitchY": "2.083",
"pitchX": "-0.000",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/53_JosephPreusser_AndresSosa.mp4",
"video_id": "21135",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/53_JosephPreusser_AndresSosa.mp4",
"pspin": "1741",
"exitvelo": "100.199"
},
{
"playID": "15051",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "",
"pitchvelo": "74.245",
"pitchY": "2.583",
"pitchX": "-0.917",
"ptype": "",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/54_JosephPreusser_AlerickSolearie.mp4",
"video_id": "21137",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/54_JosephPreusser_AlerickSolearie.mp4",
"pspin": "1712",
"exitvelo": "96.746"
},
{
"playID": "15052",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "72.624",
"pitchY": "3.333",
"pitchX": "1.000",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/55_JosephPreusser_AlerickSolearie.mp4",
"video_id": "21139",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/55_JosephPreusser_AlerickSolearie.mp4",
"pspin": "1627",
"exitvelo": "0.000"
},
{
"playID": "15053",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "",
"pitchvelo": "72.049",
"pitchY": "3.417",
"pitchX": "1.500",
"ptype": "",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/56_JosephPreusser_AlerickSolearie.mp4",
"video_id": "21141",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/56_JosephPreusser_AlerickSolearie.mp4",
"pspin": "1638",
"exitvelo": "0.000"
},
{
"playID": "15054",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Called Strike",
"pitchvelo": "71.410",
"pitchY": "2.917",
"pitchX": "-0.750",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/57_JosephPreusser_SergioMacias.mp4",
"video_id": "21143",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/57_JosephPreusser_SergioMacias.mp4",
"pspin": "1710",
"exitvelo": "0.000"
},
{
"playID": "15055",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Called Strike",
"pitchvelo": "69.017",
"pitchY": "2.167",
"pitchX": "-1.250",
"ptype": "Changeup",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/58_JosephPreusser_SergioMacias.mp4",
"video_id": "21145",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/58_JosephPreusser_SergioMacias.mp4",
"pspin": "1652",
"exitvelo": "0.000"
},
{
"playID": "15056",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Foul Ball",
"pitchvelo": "70.904",
"pitchY": "2.833",
"pitchX": "-0.750",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/59_JosephPreusser_SergioMacias.mp4",
"video_id": "21147",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/59_JosephPreusser_SergioMacias.mp4",
"pspin": "1657",
"exitvelo": "89.890"
},
{
"playID": "15057",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Contact Out",
"pitchvelo": "72.398",
"pitchY": "1.917",
"pitchX": "-0.583",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/60_JosephPreusser_SergioMacias.mp4",
"video_id": "21149",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/60_JosephPreusser_SergioMacias.mp4",
"pspin": "1727",
"exitvelo": "95.093"
},
{
"playID": "15076",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "72.540",
"pitchY": "0.417",
"pitchX": "-0.750",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/79_JosephPreusser_JakeMatheny.mp4",
"video_id": "21187",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/79_JosephPreusser_JakeMatheny.mp4",
"pspin": "1221",
"exitvelo": "0.000"
},
{
"playID": "15077",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "72.726",
"pitchY": "1.333",
"pitchX": "-0.417",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/80_JosephPreusser_JakeMatheny.mp4",
"video_id": "21189",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/80_JosephPreusser_JakeMatheny.mp4",
"pspin": "1309",
"exitvelo": "0.000"
},
{
"playID": "15078",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "73.428",
"pitchY": "3.250",
"pitchX": "1.500",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/81_JosephPreusser_JakeMatheny.mp4",
"video_id": "21191",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/81_JosephPreusser_JakeMatheny.mp4",
"pspin": "1641",
"exitvelo": "0.000"
},
{
"playID": "15079",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Called Strike",
"pitchvelo": "72.680",
"pitchY": "2.000",
"pitchX": "-0.750",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/82_JosephPreusser_JakeMatheny.mp4",
"video_id": "21193",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/82_JosephPreusser_JakeMatheny.mp4",
"pspin": "1629",
"exitvelo": "0.000"
},
{
"playID": "15080",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "71.838",
"pitchY": "3.333",
"pitchX": "1.167",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/83_JosephPreusser_JakeMatheny.mp4",
"video_id": "21195",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/83_JosephPreusser_JakeMatheny.mp4",
"pspin": "0",
"exitvelo": "0.000"
},
{
"playID": "15081",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "70.639",
"pitchY": "2.000",
"pitchX": "-1.500",
"ptype": "Slider",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/84_JosephPreusser_MarioMoralez.mp4",
"video_id": "21197",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/84_JosephPreusser_MarioMoralez.mp4",
"pspin": "1838",
"exitvelo": "0.000"
},
{
"playID": "15082",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "72.731",
"pitchY": "2.250",
"pitchX": "-2.917",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/85_JosephPreusser_MarioMoralez.mp4",
"video_id": "21199",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/85_JosephPreusser_MarioMoralez.mp4",
"pspin": "1702",
"exitvelo": "0.000"
},
{
"playID": "15083",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Called Strike",
"pitchvelo": "71.873",
"pitchY": "2.000",
"pitchX": "-0.833",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/86_JosephPreusser_MarioMoralez.mp4",
"video_id": "21201",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/86_JosephPreusser_MarioMoralez.mp4",
"pspin": "1708",
"exitvelo": "0.000"
},
{
"playID": "15084",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Contact Out",
"pitchvelo": "71.822",
"pitchY": "1.917",
"pitchX": "-0.333",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/87_JosephPreusser_MarioMoralez.mp4",
"video_id": "21203",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/87_JosephPreusser_MarioMoralez.mp4",
"pspin": "1708",
"exitvelo": "87.923"
},
{
"playID": "15085",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Single",
"pitchvelo": "73.310",
"pitchY": "2.167",
"pitchX": "-0.000",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/88_JosephPreusser_HerbertIser.mp4",
"video_id": "21205",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/88_JosephPreusser_HerbertIser.mp4",
"pspin": "1710",
"exitvelo": "94.317"
},
{
"playID": "15086",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Called Strike",
"pitchvelo": "70.570",
"pitchY": "2.667",
"pitchX": "-0.250",
"ptype": "Changeup",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/89_JosephPreusser_TyrieceSilas.mp4",
"video_id": "21207",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/89_JosephPreusser_TyrieceSilas.mp4",
"pspin": "1302",
"exitvelo": "0.000"
},
{
"playID": "15087",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "71.462",
"pitchY": "3.417",
"pitchX": "1.333",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/90_JosephPreusser_TyrieceSilas.mp4",
"video_id": "21209",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/90_JosephPreusser_TyrieceSilas.mp4",
"pspin": "1748",
"exitvelo": "0.000"
},
{
"playID": "15088",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Foul Ball",
"pitchvelo": "71.414",
"pitchY": "2.333",
"pitchX": "-0.083",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/91_JosephPreusser_TyrieceSilas.mp4",
"video_id": "21211",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/91_JosephPreusser_TyrieceSilas.mp4",
"pspin": "1870",
"exitvelo": "0.000"
},
{
"playID": "15089",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Contact Out",
"pitchvelo": "72.657",
"pitchY": "3.250",
"pitchX": "-0.333",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/92_JosephPreusser_TyrieceSilas.mp4",
"video_id": "21213",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/92_JosephPreusser_TyrieceSilas.mp4",
"pspin": "1728",
"exitvelo": "56.211"
},
{
"playID": "15090",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "73.468",
"pitchY": "1.917",
"pitchX": "1.750",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/93_JosephPreusser_MattWiseman.mp4",
"video_id": "21215",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/93_JosephPreusser_MattWiseman.mp4",
"pspin": "1761",
"exitvelo": "0.000"
},
{
"playID": "15091",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Called Strike",
"pitchvelo": "72.708",
"pitchY": "2.000",
"pitchX": "1.417",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/94_JosephPreusser_MattWiseman.mp4",
"video_id": "21217",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/94_JosephPreusser_MattWiseman.mp4",
"pspin": "1684",
"exitvelo": "0.000"
},
{
"playID": "15092",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "71.452",
"pitchY": "3.250",
"pitchX": "1.750",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/95_JosephPreusser_MattWiseman.mp4",
"video_id": "21219",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/95_JosephPreusser_MattWiseman.mp4",
"pspin": "1783",
"exitvelo": "0.000"
},
{
"playID": "15093",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "73.427",
"pitchY": "2.000",
"pitchX": "2.250",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/96_JosephPreusser_MattWiseman.mp4",
"video_id": "21221",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/96_JosephPreusser_MattWiseman.mp4",
"pspin": "1787",
"exitvelo": "0.000"
},
{
"playID": "15094",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Called Strike",
"pitchvelo": "72.897",
"pitchY": "2.833",
"pitchX": "-0.333",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/97_JosephPreusser_MattWiseman.mp4",
"video_id": "21223",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/97_JosephPreusser_MattWiseman.mp4",
"pspin": "1813",
"exitvelo": "0.000"
},
{
"playID": "15095",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Home Run",
"pitchvelo": "72.032",
"pitchY": "2.333",
"pitchX": "-0.417",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/98_JosephPreusser_MattWiseman.mp4",
"video_id": "21225",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/98_JosephPreusser_MattWiseman.mp4",
"pspin": "1737",
"exitvelo": "102.969"
},
{
"playID": "15096",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Called Strike",
"pitchvelo": "73.279",
"pitchY": "3.000",
"pitchX": "-1.000",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/99_JosephPreusser_MatthewGoodheart.mp4",
"video_id": "21227",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/99_JosephPreusser_MatthewGoodheart.mp4",
"pspin": "1676",
"exitvelo": "0.000"
},
{
"playID": "15097",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "71.102",
"pitchY": "2.667",
"pitchX": "-2.500",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/100_JosephPreusser_MatthewGoodheart.mp4",
"video_id": "21229",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/100_JosephPreusser_MatthewGoodheart.mp4",
"pspin": "1776",
"exitvelo": "0.000"
},
{
"playID": "15098",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Contact Out",
"pitchvelo": "71.473",
"pitchY": "3.250",
"pitchX": "-0.000",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/101_JosephPreusser_MatthewGoodheart.mp4",
"video_id": "21231",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/101_JosephPreusser_MatthewGoodheart.mp4",
"pspin": "1663",
"exitvelo": "98.631"
},
{
"playID": "15109",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "72.010",
"pitchY": "1.000",
"pitchX": "-0.250",
"ptype": "Changeup",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/112_JosephPreusser_AndresSosa.mp4",
"video_id": "21253",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/112_JosephPreusser_AndresSosa.mp4",
"pspin": "1754",
"exitvelo": "0.000"
},
{
"playID": "15110",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Foul Ball",
"pitchvelo": "71.821",
"pitchY": "1.667",
"pitchX": "-0.083",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/113_JosephPreusser_AndresSosa.mp4",
"video_id": "21255",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/113_JosephPreusser_AndresSosa.mp4",
"pspin": "1689",
"exitvelo": "98.554"
},
{
"playID": "15111",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Foul Ball",
"pitchvelo": "70.344",
"pitchY": "2.000",
"pitchX": "1.000",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/114_JosephPreusser_AndresSosa.mp4",
"video_id": "21257",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/114_JosephPreusser_AndresSosa.mp4",
"pspin": "1054",
"exitvelo": "0.000"
},
{
"playID": "15112",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "72.928",
"pitchY": "2.417",
"pitchX": "3.250",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/115_JosephPreusser_AndresSosa.mp4",
"video_id": "21259",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/115_JosephPreusser_AndresSosa.mp4",
"pspin": "1625",
"exitvelo": "0.000"
},
{
"playID": "15113",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "73.893",
"pitchY": "0.750",
"pitchX": "-0.583",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/116_JosephPreusser_AndresSosa.mp4",
"video_id": "21261",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/116_JosephPreusser_AndresSosa.mp4",
"pspin": "0",
"exitvelo": "0.000"
},
{
"playID": "15114",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Foul Ball",
"pitchvelo": "73.232",
"pitchY": "2.833",
"pitchX": "-0.833",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/117_JosephPreusser_AndresSosa.mp4",
"video_id": "21263",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/117_JosephPreusser_AndresSosa.mp4",
"pspin": "1753",
"exitvelo": "0.000"
},
{
"playID": "15115",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "71.560",
"pitchY": "2.250",
"pitchX": "2.000",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/118_JosephPreusser_AndresSosa.mp4",
"video_id": "21265",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/118_JosephPreusser_AndresSosa.mp4",
"pspin": "1829",
"exitvelo": "0.000"
},
{
"playID": "15116",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "72.121",
"pitchY": "4.000",
"pitchX": "2.000",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/119_JosephPreusser_AlerickSolearie.mp4",
"video_id": "21267",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/119_JosephPreusser_AlerickSolearie.mp4",
"pspin": "1690",
"exitvelo": "0.000"
},
{
"playID": "15117",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "69.411",
"pitchY": "3.333",
"pitchX": "1.750",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/120_JosephPreusser_AlerickSolearie.mp4",
"video_id": "21269",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/120_JosephPreusser_AlerickSolearie.mp4",
"pspin": "1231",
"exitvelo": "0.000"
},
{
"playID": "15118",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Double",
"pitchvelo": "71.525",
"pitchY": "2.167",
"pitchX": "-0.083",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/121_JosephPreusser_AlerickSolearie.mp4",
"video_id": "21271",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/121_JosephPreusser_AlerickSolearie.mp4",
"pspin": "1671",
"exitvelo": "77.507"
},
{
"playID": "15119",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Foul Ball",
"pitchvelo": "72.546",
"pitchY": "2.167",
"pitchX": "-0.667",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/122_JosephPreusser_SergioMacias.mp4",
"video_id": "21273",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/122_JosephPreusser_SergioMacias.mp4",
"pspin": "1668",
"exitvelo": "0.000"
},
{
"playID": "15120",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "74.601",
"pitchY": "0.917",
"pitchX": "-2.000",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/123_JosephPreusser_SergioMacias.mp4",
"video_id": "21275",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/123_JosephPreusser_SergioMacias.mp4",
"pspin": "1859",
"exitvelo": "0.000"
},
{
"playID": "15121",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "69.050",
"pitchY": "0.667",
"pitchX": "-2.250",
"ptype": "Slider",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/124_JosephPreusser_SergioMacias.mp4",
"video_id": "21277",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/124_JosephPreusser_SergioMacias.mp4",
"pspin": "1715",
"exitvelo": "0.000"
},
{
"playID": "15122",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Called Strike",
"pitchvelo": "72.810",
"pitchY": "3.333",
"pitchX": "-0.000",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/125_JosephPreusser_SergioMacias.mp4",
"video_id": "21279",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/125_JosephPreusser_SergioMacias.mp4",
"pspin": "1191",
"exitvelo": "0.000"
},
{
"playID": "15123",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Single",
"pitchvelo": "70.335",
"pitchY": "2.667",
"pitchX": "-0.583",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/126_JosephPreusser_SergioMacias.mp4",
"video_id": "21281",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/126_JosephPreusser_SergioMacias.mp4",
"pspin": "1695",
"exitvelo": "89.248"
},
{
"playID": "15124",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Foul Ball",
"pitchvelo": "71.627",
"pitchY": "1.500",
"pitchX": "1.083",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/127_JosephPreusser_JakeMatheny.mp4",
"video_id": "21283",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/127_JosephPreusser_JakeMatheny.mp4",
"pspin": "1634",
"exitvelo": "0.000"
},
{
"playID": "15125",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Foul Ball",
"pitchvelo": "69.539",
"pitchY": "2.333",
"pitchX": "-0.667",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/128_JosephPreusser_JakeMatheny.mp4",
"video_id": "21285",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/128_JosephPreusser_JakeMatheny.mp4",
"pspin": "1336",
"exitvelo": "0.000"
},
{
"playID": "15126",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Single",
"pitchvelo": "69.684",
"pitchY": "1.917",
"pitchX": "1.000",
"ptype": "Changeup",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/129_JosephPreusser_JakeMatheny.mp4",
"video_id": "21287",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/129_JosephPreusser_JakeMatheny.mp4",
"pspin": "1324",
"exitvelo": "96.733"
},
{
"playID": "15127",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Called Strike",
"pitchvelo": "70.360",
"pitchY": "1.667",
"pitchX": "-0.500",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/130_JosephPreusser_MarioMoralez.mp4",
"video_id": "21289",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/130_JosephPreusser_MarioMoralez.mp4",
"pspin": "1746",
"exitvelo": "0.000"
},
{
"playID": "15128",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "71.827",
"pitchY": "2.500",
"pitchX": "-1.417",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/131_JosephPreusser_MarioMoralez.mp4",
"video_id": "21291",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/131_JosephPreusser_MarioMoralez.mp4",
"pspin": "1694",
"exitvelo": "0.000"
},
{
"playID": "15129",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Foul Ball",
"pitchvelo": "72.179",
"pitchY": "2.417",
"pitchX": "-0.917",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/132_JosephPreusser_MarioMoralez.mp4",
"video_id": "21293",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/132_JosephPreusser_MarioMoralez.mp4",
"pspin": "1653",
"exitvelo": "86.525"
},
{
"playID": "15130",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Contact Out",
"pitchvelo": "71.520",
"pitchY": "2.250",
"pitchX": "-1.250",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/133_JosephPreusser_MarioMoralez.mp4",
"video_id": "21295",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/133_JosephPreusser_MarioMoralez.mp4",
"pspin": "1754",
"exitvelo": "80.995"
},
{
"playID": "15131",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Single",
"pitchvelo": "70.142",
"pitchY": "2.333",
"pitchX": "-0.083",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/134_JosephPreusser_HerbertIser.mp4",
"video_id": "21297",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/134_JosephPreusser_HerbertIser.mp4",
"pspin": "1668",
"exitvelo": "75.751"
},
{
"playID": "15132",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Called Strike",
"pitchvelo": "72.556",
"pitchY": "1.667",
"pitchX": "-0.583",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/135_JosephPreusser_TyrieceSilas.mp4",
"video_id": "21299",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/135_JosephPreusser_TyrieceSilas.mp4",
"pspin": "1586",
"exitvelo": "0.000"
},
{
"playID": "15133",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Ball",
"pitchvelo": "70.459",
"pitchY": "4.083",
"pitchX": "-0.167",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/136_JosephPreusser_TyrieceSilas.mp4",
"video_id": "21301",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/136_JosephPreusser_TyrieceSilas.mp4",
"pspin": "1718",
"exitvelo": "0.000"
},
{
"playID": "15134",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Contact Out",
"pitchvelo": "68.208",
"pitchY": "2.667",
"pitchX": "-0.500",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/137_JosephPreusser_TyrieceSilas.mp4",
"video_id": "21303",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/137_JosephPreusser_TyrieceSilas.mp4",
"pspin": "1750",
"exitvelo": "92.306"
},
{
"playID": "15135",
"eventName": "San Jac vs South Suburban CC",
"eventDate": "13/3/2018 17:01:48",
"eventID": "32",
"name": "Joseph Preusser",
"presult": "Contact Out",
"pitchvelo": "70.271",
"pitchY": "1.417",
"pitchX": "-0.667",
"ptype": "Fastball",
"angle1": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCC/138_JosephPreusser_MattWiseman.mp4",
"video_id": "21305",
"angle2": "https://s3.amazonaws.com/prospectwire/2018-03-13/SanJacintoCCvsSouthSuburbanCCAngle2/138_JosephPreusser_MattWiseman.mp4",
"pspin": "1636",
"exitvelo": "92.327"
}
]
}
.fa-rotate-45 {
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}
.fa-rotate-315 {
-webkit-transform: rotate(315deg);
-moz-transform: rotate(315deg);
-ms-transform: rotate(315deg);
-o-transform: rotate(315deg);
transform: rotate(315deg);
}
.green{
color: limegreen;
}
table {
border-collapse: collapse;
}
.table {
width: 100%;
max-width: 100%;
margin-bottom: 1rem;
background-color: transparent;
font-size: 0.92rem;
}
.table thead th {
vertical-align: bottom;
border-bottom: 2px solid #e9ecef;
}
.table tr.active{
background-color: #c5e8f5;
}
.table td, .table th {
padding: .3rem;
vertical-align: top;
border-top: 1px solid #e9ecef;
text-align: center;
}
.table td, .table th {
padding: .3rem;
vertical-align: top;
border-top: 1px solid #e9ecef;
}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
vertical-align: middle;
margin-bottom: 0;
}
.switch input {display:none;}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #2196F3;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
/* module specific styles */
.pitch-ball.active{
stroke: #ee83d8;
stroke-width: 8;
}
.pitch-ball.hovered{
fill: #c6c6c6;
}
div.pitch-tooltip {
position: absolute;
text-align: center;
width: 70px;
height: 28px;
line-height: 28px;
padding: 6px 2px 2px 2px;
font: 12px sans-serif;
background: black;
border: 0px;
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
pointer-events: none;
color:white;
z-index: 2;
}
#pitch-video-module{
}
#pitch-chart-module {
position: relative;
}
#pitch-chart-module svg{
position: relative;
z-index: 1;
}
.pitch-heatmap-wrapper{
position: absolute;
top: 0;
left: 0;
z-index: 0;
}
.pitch-heatmap {
position: relative;
width: 100%;
height: 100%;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment