Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement direction reconstruction web app #46

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7df59a8
Started work on direction reconstruction
davidfokkema Mar 16, 2015
4da4a8b
Completely disable zooming and panning
davidfokkema Mar 30, 2015
493b1e8
Revert "Completely disable zooming and panning"
davidfokkema Mar 30, 2015
97a561d
Do not use CSS transforms
davidfokkema Mar 30, 2015
d7bdaf9
Use a nicer coincidence
davidfokkema Mar 30, 2015
9d48d7f
Draw shower front
davidfokkema Apr 13, 2015
e678f7b
Draw front a center of cluster
davidfokkema Apr 13, 2015
a3eaa3f
Gutted version works with drag
davidfokkema May 18, 2015
51db9f0
Stations are back again!
davidfokkema May 18, 2015
14b307e
Front is back!
davidfokkema May 18, 2015
f06af76
Rotation of shower front!
davidfokkema May 18, 2015
9ec3abe
Distances work as well!
davidfokkema May 18, 2015
c620661
Disable zoom
davidfokkema Jun 8, 2015
6c3ff8e
Disable dragging
davidfokkema Jun 8, 2015
4f0c056
Center two plots side by side
davidfokkema Jun 8, 2015
dadcdcb
Started work on adding LDF plot
davidfokkema Jun 8, 2015
fa1a925
Fix misleading comment
davidfokkema Jun 15, 2015
59658b6
Some UI improvements
davidfokkema Jun 15, 2015
61ce019
LDF: scale y axis using timestamp
davidfokkema Jun 15, 2015
bece7ee
Autoscale y-axis
davidfokkema Jun 15, 2015
37d34b5
Show direction of shower
davidfokkema Jun 15, 2015
dfcc5e8
Fix distance sign
davidfokkema Jun 24, 2015
1184970
Add axis labels and improve layout
davidfokkema Jun 26, 2015
00ccf07
Calculate physical distance
davidfokkema Jun 26, 2015
dc55db5
Smaller size for stations
davidfokkema Jun 26, 2015
9344480
Added next button for new events
davidfokkema Jun 29, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions direction-reconstruction/.gitignore
@@ -0,0 +1,2 @@
*.h5
*.json
316 changes: 316 additions & 0 deletions direction-reconstruction/code.js
@@ -0,0 +1,316 @@
var station_info;
var next_event;

var FRONT_LENGTH = 1000;
var ROTATE_LENGTH = 100;

var map = L.map('map', { scrollWheelZoom: false, zoomControl: false,
dragging: false });
var svg = d3.select(map.getPanes().overlayPane).append("svg"),
g = svg.append("g").attr("class", "leaflet-zoom-hide");

var drag_core = d3.behavior.drag()
.origin(function() {
var fd = front.datum();
return map.latLngToContainerPoint([fd.lat, fd.lng]); })
.on("drag", move_core);
// .on("dragstart", function() { map.dragging.disable(); })
// .on("dragend", function() { map.dragging.enable(); });

var drag_alpha = d3.behavior.drag()
.origin(function() {
var x = front_rotate_handle.attr("cx");
var y = front_rotate_handle.attr("cy");
return map.layerPointToContainerPoint([x, y]); })
.on("drag", rotate_front);
// .on("dragstart", function() { map.dragging.disable(); })
// .on("dragend", function() { map.dragging.enable(); });

var stations = g.selectAll('.station');
var distances = g.selectAll('.distance');
var distance_labels = g.selectAll('.distance_label');

var front = g.append("line")
.datum({ 'lat': 0, 'lng': 0, 'alpha': Math.PI / 8 })
.attr("id", "front");
var direction = g.append("line")
.attr("id", "direction");
var core = g.append("circle")
.attr("r", 7)
.call(drag_core);
var front_rotate_handle = g.append("circle")
.attr("r", 5)
.attr("fill", 'white')
.attr("stroke", 'black')
.call(drag_alpha);


// LDF plots
var fullwidth = 400,
fullheight = 300;
var margin = {top: 20, right: 20, bottom: 50, left: 50};
var width = fullwidth - margin.left - margin.right,
height = fullheight - margin.top - margin.bottom;

var key_idx = 0;

var x_scale = d3.scale.linear()
.range([0, width])
.domain([-300, 300]);
var y_scale = d3.scale.linear()
.range([height, 0]);

var ldf_svg = d3.select("#ldf").append("svg")
.attr("width", fullwidth)
.attr("height", fullheight)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

ldf_svg.append("rect")
.attr("class", "data_rectangle")
.attr("width", width)
.attr("height", height);

var xAxis = d3.svg.axis()
.scale(x_scale)
.ticks(5)
.orient("bottom");

var yAxis = d3.svg.axis()
.scale(y_scale)
.ticks(5)
.orient("left");

ldf_svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.style("text-anchor", "middle")
.attr("x", width / 2)
.attr("y", 40)
.text("distance [m]");

ldf_svg.append("g")
.attr("class", "y axis")
.append("text")
.attr("transform", "rotate(-90)")
.style("text-anchor", "middle")
.attr("x", -height / 2)
.attr("y", -35)
.text("Δt [ns]");

var arrival_times = ldf_svg.selectAll("circle");
var timeline = ldf_svg.select("#timeline");


function x(coord) { return map.latLngToLayerPoint(coord).x; }
function y(coord) { return map.latLngToLayerPoint(coord).y; }

function update_layer_position() {
// update layer's position to top-left of map container
var pos = map.containerPointToLayerPoint([0, 0]);
L.DomUtil.setPosition(svg.node(), pos);

// if you reposition the overlay, translate it with the negative offset to
// be able to use the conversion functions.
g.attr("transform", "translate(" + -pos.x + "," + -pos.y + ")");

// reposition all circles
g.selectAll(".station")
.each(function(d) {
d.x = x(station_info[d.station]);
d.y = y(station_info[d.station]);
})
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });

// update shower front position
update_shower_front();
}

map.on('moveend', update_layer_position);

function marker_size(event) {
num_particles = event.n1 + event.n2 + event.n3 + event.n4;
log_particles = Math.log10(1 + num_particles);
size = 7 * Math.sqrt(log_particles);
return size;
}

function update_coincidence(coincidences) {
var c_idx = 1;

function update() {
c_idx ++;
var events = coincidences[c_idx].events;
events.forEach(function (value) {
value.key = 'c-' + c_idx + '-' + value.station; });

stations = g.selectAll('.station')
.data(events, function(d) { return d.key; });

stations
.each(function() {
console.warn("Updated an element."); });

stations.enter().insert("circle", ":first-child")
.attr("class", "station")
.style("opacity", 1)
.attr("r", 0)
.transition()
.attr("r", function(d) { return marker_size(d); });
stations.exit().remove();

distances = g.selectAll('.distance')
.data(events, function(d) { return d.key; });
distances.enter().insert("line", ":first-child")
.attr("class", "distance");
distances.exit().remove();

distance_labels = g.selectAll('.distance_label')
.data(events, function(d) { return d.key; });
distance_labels.enter().append("text")
.attr("class", "distance_label");
distance_labels.exit().remove();

arrival_times = ldf_svg.selectAll("circle")
.data(events, function(d) { return d.key; });
arrival_times.enter().append("circle")
.attr("r", 5);
arrival_times.exit().remove();

y_scale.domain([0, d3.max(events, function(d) { return d.t; })]);
d3.select(".y.axis").call(yAxis);

update_layer_position();
}

update();
return update;
}

d3.json('./stations.json', function(error, data) {
station_info = data;
data = Object.keys(data).map(function (value, index, array) {
return Array(value).concat(data[value]);
});

function lat(d) { return d[1]; }
function lon(d) { return d[2]; }

lat_min = d3.min(data, lat);
lat_mean = d3.mean(data, lat);
lat_max = d3.max(data, lat);
lon_min = d3.min(data, lon);
lon_mean = d3.mean(data, lon);
lon_max = d3.max(data, lon);

L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">' +
'OpenStreetMap</a> contributors, ' +
'&copy; <a href="http://cartodb.com/attributions">CartoDB</a>',
}).addTo(map);

map.fitBounds([[lat_min, lon_min], [lat_max, lon_max]]);

front.datum().lat = lat_mean;
front.datum().lng = lon_mean;

d3.json('./coincidences.json', function(error, data) {
next_event = update_coincidence(data);
});
});

function front_line_x(fd, dist) { return fd.x + dist * Math.cos(fd.alpha); }
function front_line_y(fd, dist) { return fd.y + dist * Math.sin(fd.alpha); }

function update_shower_front() {
var fd = front.datum();

layer_pos = map.latLngToLayerPoint([fd.lat, fd.lng]);
fd.x = layer_pos.x;
fd.y = layer_pos.y;

front
.attr("x1", front_line_x(fd, -FRONT_LENGTH))
.attr("y1", front_line_y(fd, -FRONT_LENGTH))
.attr("x2", front_line_x(fd, FRONT_LENGTH))
.attr("y2", front_line_y(fd, FRONT_LENGTH));

direction
.attr("x1", fd.x)
.attr("y1", fd.y)
.attr("x2", fd.x + 100 * Math.cos(fd.alpha - Math.PI / 2))
.attr("y2", fd.y + 100 * Math.sin(fd.alpha - Math.PI / 2));

core.attr("cx", x([fd.lat, fd.lng]))
.attr("cy", y([fd.lat, fd.lng]));

front_rotate_handle
.attr("cx", front_line_x(fd, ROTATE_LENGTH))
.attr("cy", front_line_y(fd, ROTATE_LENGTH));

distances.each(calculate_distances);

distances
.attr("x1", function(d) { return d.x; })
.attr("y1", function(d) { return d.y; })
.attr("x2", function(d) { return d.xp; })
.attr("y2", function(d) { return d.yp; });

distance_labels
.attr("x", function(d) { return d.label_x + 5; })
.attr("y", function(d) { return d.label_y + 5; })
.text(function(d) { return d.dist.toFixed(); });

arrival_times
.attr("cx", function(d) { return x_scale(d.dist); })
.attr("cy", function(d) { return y_scale(d.t); });

// x_scale.domain([0, d3.max(distances.data(), function(d) { return d.dist; })]);
// d3.select(".x.axis").call(xAxis);
}

function move_core() {
var fd = front.datum();

container_pos = [d3.event.x, d3.event.y];
latlng = map.containerPointToLatLng(container_pos);
fd.lat = latlng.lat;
fd.lng = latlng.lng;
distances.each(calculate_distances);
update_shower_front();
}

function calculate_distances(d) {
var fd = front.datum();
d.r = (d.x - fd.x) * Math.cos(fd.alpha) + (d.y - fd.y) * Math.sin(fd.alpha);
d.xp = front_line_x(fd, d.r);
d.yp = front_line_y(fd, d.r);
// d.dist = Math.sqrt(Math.pow(d.x - d.xp, 2) + Math.pow(d.y - d.yp, 2));
d.dist = map.layerPointToLatLng([d.x, d.y])
.distanceTo(map.layerPointToLatLng([d.xp, d.yp]));

beta = Math.atan2(d.y - fd.y, d.x - fd.x);
delta = beta - fd.alpha;
delta = (delta + Math.PI) % (2 * Math.PI) - Math.PI;
if (-Math.PI <= delta && delta < 0) {
d.dist *= -1;
}

d.label_x = (d.x + d.xp) / 2;
d.label_y = (d.y + d.yp) / 2;
}

function rotate_front() {
var fd = front.datum();
var container_pos = map.latLngToContainerPoint([fd.lat, fd.lng]);
var x0 = container_pos.x;
var y0 = container_pos.y;
fd.alpha = Math.atan2(d3.event.y - y0, d3.event.x - x0);
distances.each(calculate_distances);
update_shower_front();
}

distances.each(calculate_distances);