532 lines
14 KiB
JavaScript
Executable File
532 lines
14 KiB
JavaScript
Executable File
|
|
//var width = 700,
|
|
// height = 800,
|
|
var vBoxWidth = 1200,
|
|
vBoxHeight = 900,
|
|
bgWidth = 900,
|
|
bgHeight = 1500,
|
|
clicked;
|
|
|
|
//Object used for local storage of the user selections - had to use underscores instead of hyphen
|
|
/*
|
|
var pageData = {
|
|
deathmatch_list: 0,
|
|
time_since_spawn: 10,
|
|
}
|
|
*/
|
|
|
|
//var interpolationColours = ["#55ff00", "#fffc00", "#ff0400"]; // green - yellow - red
|
|
|
|
function convert2WebXCoord(x) {
|
|
return ((x + project.map.coords.x)/project.map.scale);
|
|
}
|
|
function convert2WebYCoord(y) {
|
|
return (((-1)*y + project.map.coords.y)/project.map.scale);
|
|
}
|
|
function convert2WebCoords(p) {
|
|
return [convert2WebXCoord(p[0]), convert2WebYCoord(p[1])];
|
|
}
|
|
|
|
/*
|
|
function convertFromWebXCoord(x) {
|
|
return (-1) * ((project.map.coords.x) - (x*project.map.scale));
|
|
}
|
|
function convertFromWebYCoord(y) {
|
|
return ((project.map.coords.y) - (y*project.map.scale));
|
|
}
|
|
*/
|
|
|
|
/*
|
|
function updateMapHeight() {
|
|
// Adjust the height of the map automatically
|
|
var windowHeight = $("#content").height() - 20;
|
|
//$("#map-wrapper").css("height", windowHeight);
|
|
$("#map").css("height", windowHeight);
|
|
$("#map").css("width", "70%");
|
|
$("#map-side-options").css("height", windowHeight);
|
|
}
|
|
*/
|
|
|
|
var headerAndFilters = {
|
|
headerType: "header-sc-map", // social club filtering header
|
|
disabledFields: // disables header fields
|
|
[
|
|
false, // platforms
|
|
false, // locations
|
|
false, // age
|
|
false, // gamers
|
|
false, // game-types
|
|
false, // dates+builds
|
|
],
|
|
};
|
|
|
|
function initPage() {
|
|
initHeaderAndFilters(headerAndFilters);
|
|
|
|
//var localData = retrieveLocalObject(config.currentFilename);
|
|
//pageData = (localData) ? localData : pageData;
|
|
|
|
//pageData.deathmatch_list
|
|
|
|
$("#content-body")
|
|
.append(
|
|
$("<div>")
|
|
.attr("id", "map-wrapper")
|
|
.append(
|
|
$("<fieldset>")
|
|
.attr("id", "map")
|
|
.append(
|
|
$("<legend>").text("Map")
|
|
)
|
|
)
|
|
.append(
|
|
$("<div>")
|
|
.attr("id", "map-side-options")
|
|
)
|
|
);
|
|
|
|
block();
|
|
$.ajax({
|
|
url: project.map.svgFile,
|
|
type: "GET",
|
|
data: {},
|
|
dataType: "xml",
|
|
async: false,
|
|
|
|
success: function(xml, textStatus, jqXHR) {
|
|
var importedNode;
|
|
try {
|
|
importedNode = document.importNode(xml.documentElement, true);
|
|
}
|
|
catch(e) {
|
|
// IE case
|
|
importedNode = ieImportNode(xml.documentElement, document);
|
|
}
|
|
|
|
$("#map").append(importedNode);
|
|
},
|
|
error: function (xhr, ajaxOptions, thrownError){
|
|
console.error(this.url + "\n" + ajaxOptions + " " + xhr.status + " " + thrownError );
|
|
},
|
|
complete: function() {
|
|
var svg = d3.select("#map svg")
|
|
.attr("width", $("#map").width())
|
|
.attr("height", $("#map").height())
|
|
.attr("enable-background", "new 0 0 " + bgWidth + " "+ bgHeight)
|
|
.call(svg_interact);
|
|
|
|
drawGrid();
|
|
}
|
|
});
|
|
unBlock();
|
|
|
|
|
|
$("#map-side-options")
|
|
.append(
|
|
$("<fieldset>")
|
|
.attr("id", "map-layers")
|
|
.append(
|
|
$("<legend>").text("Map Layers")
|
|
)
|
|
.append(
|
|
$("<input>")
|
|
.attr("type", "checkbox")
|
|
.attr("id", "map-layer-grid")
|
|
.attr("checked", true)
|
|
.val("grid")
|
|
)
|
|
.append(
|
|
$("<label>")
|
|
.attr("for", "map-layer-grid")
|
|
.text("Grid")
|
|
)
|
|
.append($("<br />"))
|
|
.append(
|
|
$("<input>")
|
|
.attr("type", "checkbox")
|
|
.attr("id", "map-layer-sea")
|
|
.attr("checked", true)
|
|
.val("sea")
|
|
)
|
|
.append(
|
|
$("<label>")
|
|
.attr("for", "map-layer-sea")
|
|
.text("Sea")
|
|
)
|
|
.append($("<br />"))
|
|
.append(
|
|
$("<input>")
|
|
.attr("type", "checkbox")
|
|
.attr("id", "map-layer-landcity")
|
|
.attr("checked", true)
|
|
.val("landcity")
|
|
)
|
|
.append(
|
|
$("<label>")
|
|
.attr("for", "map-layer-landcity")
|
|
.text("Landcity")
|
|
)
|
|
.append($("<br />"))
|
|
.append(
|
|
$("<input>")
|
|
.attr("type", "checkbox")
|
|
.attr("id", "map-layer-landbeach")
|
|
.attr("checked", true)
|
|
.val("landbeach")
|
|
)
|
|
.append(
|
|
$("<label>")
|
|
.attr("for", "map-layer-landbeach")
|
|
.text("Land Beach")
|
|
)
|
|
.append($("<br />"))
|
|
.append(
|
|
$("<input>")
|
|
.attr("type", "checkbox")
|
|
.attr("id", "map-layer-landgreen")
|
|
.attr("checked", true)
|
|
.val("landgreen")
|
|
)
|
|
.append(
|
|
$("<label>")
|
|
.attr("for", "map-layer-landgreen")
|
|
.text("Landgreen")
|
|
)
|
|
.append($("<br />"))
|
|
.append(
|
|
$("<input>")
|
|
.attr("type", "checkbox")
|
|
.attr("id", "map-layer-tunnels")
|
|
.attr("checked", true)
|
|
.val("tunnels")
|
|
)
|
|
.append(
|
|
$("<label>")
|
|
.attr("for", "map-layer-tunnels")
|
|
.text("Tunnels")
|
|
)
|
|
.append($("<br />"))
|
|
.append(
|
|
$("<input>")
|
|
.attr("type", "checkbox")
|
|
.attr("id", "map-layer-metro")
|
|
.attr("checked", true)
|
|
.val("metro")
|
|
)
|
|
.append(
|
|
$("<label>")
|
|
.attr("for", "map-layer-metro")
|
|
.text("Metro")
|
|
)
|
|
.append($("<br />"))
|
|
.append(
|
|
$("<input>")
|
|
.attr("type", "checkbox")
|
|
.attr("id", "map-layer-roads")
|
|
.attr("checked", true)
|
|
.val("roads")
|
|
)
|
|
.append(
|
|
$("<label>")
|
|
.attr("for", "map-layer-roads")
|
|
.text("Roads")
|
|
)
|
|
.append($("<br />"))
|
|
.append(
|
|
$("<input>")
|
|
.attr("type", "checkbox")
|
|
.attr("id", "map-layer-rail")
|
|
.attr("checked", true)
|
|
.val("rail")
|
|
)
|
|
.append(
|
|
$("<label>")
|
|
.attr("for", "map-layer-rail")
|
|
.text("Rail")
|
|
)
|
|
.append($("<br />"))
|
|
.append(
|
|
$("<input>")
|
|
.attr("type", "checkbox")
|
|
.attr("id", "map-layer-telemetry")
|
|
.attr("checked", true)
|
|
.val("telemetry")
|
|
)
|
|
.append(
|
|
$("<label>")
|
|
.attr("for", "map-layer-telemetry")
|
|
.text("Telemetry")
|
|
)
|
|
.append($("<br />"))
|
|
);
|
|
|
|
// End of adding grid layers
|
|
|
|
/*
|
|
// Bind the events
|
|
$(".overlay-options select").change(function() {
|
|
pageData[$(this).attr("id").replace(/\-/g, '_')] = $(this).val();
|
|
storeLocalObject(config.currentFilename, pageData);
|
|
|
|
generateCustomOverlay();
|
|
});
|
|
*/
|
|
|
|
/*
|
|
$("input#time-since-spawn").change(function() {
|
|
pageData[$(this).attr("id").replace(/\-/g, '_')] = $(this).val();
|
|
storeLocalObject(config.currentFilename, pageData);
|
|
|
|
generateCustomOverlay();
|
|
});
|
|
*/
|
|
|
|
//$("#map-layers :checkbox").click(function(){
|
|
// The following declaration is faster than the latter
|
|
|
|
|
|
$("#map-layers input[type=checkbox]").click(function() {
|
|
var layer = d3.select("#map svg").select("g#" + $(this).val());
|
|
|
|
if ($(this).is(":checked")) {
|
|
layer
|
|
.transition()
|
|
.attr("opacity", 1);
|
|
}
|
|
else {
|
|
layer
|
|
.transition()
|
|
.attr("opacity", 0.01);
|
|
}
|
|
});
|
|
|
|
//updateMapHeight();
|
|
//$(window).resize(updateMapHeight);
|
|
|
|
$("#filter").click(function() {
|
|
generateCustomOverlay();
|
|
});
|
|
|
|
generateCustomOverlay();
|
|
}
|
|
|
|
function drawGrid() {
|
|
var gridStep = 500/project.map.scale; // The step of the grid in map coordinates
|
|
|
|
var grid = d3.select("#map svg")
|
|
.append("svg:g")
|
|
.attr("id", "grid");
|
|
|
|
var xAxisDataNeg = d3.range(convert2WebXCoord(0), 0, -gridStep);
|
|
var xAxisDataPos = d3.range(convert2WebXCoord(0), bgWidth, gridStep);
|
|
|
|
var yAxisDataNeg = d3.range(convert2WebYCoord(0), 0, -gridStep);
|
|
var yAxisDataPos = d3.range(convert2WebYCoord(0), bgHeight, gridStep);
|
|
|
|
grid.selectAll("line.vertical")
|
|
.data(xAxisDataNeg.concat(xAxisDataPos))
|
|
.enter()
|
|
.append("svg:line")
|
|
.attr("x1", function(d) {return d;})
|
|
.attr("y1", 0)
|
|
.attr("x2", function(d) {return d;})
|
|
.attr("y2", bgHeight)
|
|
.attr("class", "map-grid");
|
|
|
|
grid.selectAll("line.horizontal")
|
|
.data(yAxisDataNeg.concat(yAxisDataPos))
|
|
.enter()
|
|
.append("svg:line")
|
|
.attr("x1", 0)
|
|
.attr("y1", function(d) {return d;})
|
|
.attr("x2", bgWidth)
|
|
.attr("y2", function(d) {return d;})
|
|
.attr("class", "map-grid");
|
|
}
|
|
|
|
function drawSpawnHeatmap() {
|
|
/*
|
|
var radius = 2,
|
|
UGCId = $("select#deathmatch-list").val(),
|
|
timeSinceSpawn = $("input#time-since-spawn").val();
|
|
|
|
// get the social club header filtering parameter values, headerAndFilters.headerType comes from local conf
|
|
var pValues = config.headerOptions[headerAndFilters.headerType].getParamValues();
|
|
console.log(pValues;
|
|
|
|
// Add the extra Spaw Points param values
|
|
pValues.Pairs["DeathmatchUGCIdentifier"] = UGCId;
|
|
pValues.Pairs["BlockSize"] = radius;
|
|
pValues.Pairs["TimeSinceSpawn"] = timeSinceSpawn;
|
|
*/
|
|
var pValues = config.headerOptions[headerAndFilters.headerType].getParamValues();
|
|
if (!pValues)
|
|
return;
|
|
//console.log(pValues);
|
|
//console.log(pValues.Pairs["BlockSize"]);
|
|
|
|
if (!pValues.Pairs["DeathmatchUGCIdentifier"] || !pValues.Pairs["TimeSinceSpawn"])
|
|
return;
|
|
|
|
var req = new ReportRequest(config.restHost + config.mapDeathmatchStats,
|
|
"json",
|
|
config.restHost + config.mapDeathmatchStatsAsync + pValues.ForceUrlSuffix,
|
|
config.restHost + config.reportsQueryAsync);
|
|
|
|
req.sendSingleAsyncRequest(pValues, handleSpawnResults);
|
|
|
|
|
|
function handleSpawnResults(telemetryData) {
|
|
showMsgWhenNoData(telemetryData);
|
|
if (telemetryData.length == 0)
|
|
return;
|
|
|
|
var valueFunc = function(d) {return d.Occurrences};
|
|
var xFunc = function(d) {return d.Location.X};
|
|
var yFunc = function(d) {return d.Location.Y};
|
|
|
|
generateCoolHeatmap(telemetryData, pValues.Pairs["BlockSize"], valueFunc, xFunc, yFunc);
|
|
}
|
|
}
|
|
|
|
function generateCoolHeatmap(telemetryData, radius, valueFunc, xFunc, yFunc) {
|
|
// Clean any canvas elements
|
|
$("#map canvas").remove();
|
|
|
|
var config = {
|
|
"radius": 10, // This is only for heatmap.js lib, different than the blocksize
|
|
"element": "map",
|
|
"visible": true,
|
|
"opacity": 60,
|
|
"gradient": { 0: "rgb(0,255,0)", 0.5: "yellow", 1: "rgb(255,0,0)" }
|
|
};
|
|
|
|
var heatmap = heatmapFactory.create(config);
|
|
|
|
var heatmapData = {
|
|
max: 0,
|
|
data: [],
|
|
};
|
|
|
|
// Var for the max value
|
|
var max = 0;
|
|
// We need to find the rectangle that includes all the points so we can generate only a small
|
|
// heatmap image of the desired area.
|
|
var upperLeft = {x: telemetryData[0].Location.X, y: telemetryData[0].Location.Y};
|
|
var lowerRight = {x: telemetryData[0].Location.X, y: telemetryData[0].Location.Y};
|
|
|
|
$.each(telemetryData, function(i, point) {
|
|
// find the maximum value
|
|
max = (max < valueFunc(point)) ? valueFunc(point) : max;
|
|
// We need to find the upperLeft and lowerRight points in real game coordinates that include all the points
|
|
// upperLeft has minimum X and maximum Y
|
|
upperLeft.x = (xFunc(point) < upperLeft.x) ? xFunc(point) : upperLeft.x;
|
|
upperLeft.y = (yFunc(point) > upperLeft.y) ? yFunc(point) : upperLeft.y;
|
|
// lowerRight has maximum X and minimum or equal Y
|
|
lowerRight.x = (xFunc(point) > lowerRight.x) ? xFunc(point) : lowerRight.x;
|
|
lowerRight.y = (yFunc(point) < lowerRight.y) ? yFunc(point) : lowerRight.y;
|
|
});
|
|
// Save the max value to the heatmap data
|
|
heatmapData["max"] = max;
|
|
|
|
var margin = 50;
|
|
|
|
// Add the radius + margin to the rectangle corner points
|
|
upperLeft.x -= (radius + margin);
|
|
upperLeft.y += (radius + margin);
|
|
lowerRight.x += (radius + margin);
|
|
lowerRight.y -= (radius + margin);
|
|
|
|
//console.log(max);
|
|
//console.log(upperLeft);
|
|
//console.log(lowerRight);
|
|
|
|
// The width and the height of the heatmap fragment will be
|
|
fragmentWidth = lowerRight.x - upperLeft.x;
|
|
fragmentHeight = upperLeft.y - lowerRight.y;
|
|
//console.log(fragmentWidth);
|
|
//console.log(fragmentHeight);
|
|
|
|
// Crate a new fragment coordinates system where 0.0 is the top left
|
|
// new (0, 0) will be (upperLeft.x, upperLeft.y)
|
|
function convertXtoFragmentCoords(x) {
|
|
// Subtract new (0, 0) from the x coord, upperLeft.x is the minimum x
|
|
return x - upperLeft.x;
|
|
}
|
|
function convertYtoFragmentCoords(y) {
|
|
// Subtract y coord from the new (0, 0), upperLeft.y is the maximum y
|
|
return upperLeft.y - y;
|
|
}
|
|
|
|
$("#map canvas")
|
|
.attr("id", "heatmapjs-canvas")
|
|
.attr("width", fragmentWidth)
|
|
.attr("height", fragmentHeight)
|
|
.css("display", "none");
|
|
|
|
$.each(telemetryData, function(i, point) {
|
|
heatmapData.data.push({x: convertXtoFragmentCoords(xFunc(point)),
|
|
y: convertYtoFragmentCoords(yFunc(point)),
|
|
count: valueFunc(point)});
|
|
})
|
|
heatmap.store.setDataSet(heatmapData);
|
|
//console.log(heatmapData);
|
|
|
|
// Save the canvas to a file object as png image
|
|
var canvas = document.getElementById("heatmapjs-canvas");
|
|
var image = canvas.toDataURL("image/png");
|
|
//window.location = image;
|
|
|
|
// Add the scaled image to the correct position on the svg map (use the previously calculated upperLeft point)
|
|
var imageHeatmap =
|
|
d3.select("#map svg")
|
|
.append("svg:g")
|
|
.attr("id", "telemetry")
|
|
|
|
imageHeatmap.append("svg:g")
|
|
.append("svg:image")
|
|
.attr("xlink:href", image)
|
|
.attr("width", fragmentWidth/project.map.scale)
|
|
.attr("height", fragmentHeight/project.map.scale)
|
|
.attr("x", function() {return convert2WebXCoord(upperLeft.x)})
|
|
.attr("y", function() {return convert2WebYCoord(upperLeft.y)});
|
|
|
|
imageHeatmap
|
|
.append("svg:g")
|
|
.append("svg:rect")
|
|
.attr("x", function() {
|
|
return convert2WebXCoord(upperLeft.x);
|
|
})
|
|
.attr("y", function() {
|
|
return convert2WebYCoord(upperLeft.y);
|
|
})
|
|
.attr("width", fragmentWidth/project.map.scale)
|
|
.attr("height", fragmentHeight/project.map.scale)
|
|
.attr("stroke", config.chartColour1)
|
|
.attr("stroke-width", 2)
|
|
.attr("fill-opacity", 0.3)
|
|
.append("title")
|
|
.text(function() {
|
|
return $("select#deathmatch-list :selected").html();
|
|
})
|
|
}
|
|
// end of generateCoolHeatmap
|
|
|
|
function removeOverlay() {
|
|
d3.select("#map svg")
|
|
.selectAll("g#telemetry")
|
|
.transition()
|
|
.attr("opacity", 0.01)
|
|
.remove();
|
|
}
|
|
|
|
function generateCustomOverlay() {
|
|
removeOverlay();
|
|
drawSpawnHeatmap();
|
|
}
|
|
|
|
function showMsgWhenNoData(array) {
|
|
if (array.length < 1)
|
|
Sexy.alert(config.noDataMapText);
|
|
}
|