Commit cc5888fd by Tuomas Riihimäki

Initial version of new d3 map by annttu

1 parent bd707b7d
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:map="http://java.sun.com/jsf/composite/cditools/map"
xmlns:tools="http://java.sun.com/jsf/composite/cditools"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:p="http://primefaces.org/ui">
<h:body>
<ui:composition template="#{sessionHandler.template}">
<f:metadata>
<f:event type="preRenderView" listener="#{ajaxMapView.initMap()}" />
<f:viewParam name="userid" value="#{userView.userid}" />
</f:metadata>
<ui:param name="thispage" value="page.place.placemap" />
<ui:define name="content">
<button
onclick="alert('pre');placeClicker([{name:'placeId', value:1}]);alert('post');">button</button>
<h:form>
<p:remoteCommand name="placeClicker" update=":fbdiag"
action="#{ajaxMapView.placeClicked()}" />
</h:form>
<p:dialog visible="#{!empty ajaxMapView.place}" id="fbdiag">
Clicked place name : #{ajaxMapView.place.name};
<h:link rendered="#{!empty ajaxMapView.place}" outcome="/place/edit">
<f:param name="placeid" value="#{ajaxMapView.place.id}" />
Muokkaa
</h:link>
</p:dialog>
<h:outputScript target="head" library="seatjs" name="d3.min.js" />
<h:outputScript target="head" library="seatjs" name="d3-tip.js" />
<h:outputScript target="head" library="seatjs" name="seatmap.js" />
<h:outputStylesheet library="seatjs" name="placemap.css" />
<svg id="seatmap" style="margin: auto; border: 1px solid black;"
width="1200px" height="800px" />
<script type="text/javascript">
px = placemap({
element : document.getElementById("seatmap"),
moyaurl : "#{request.contextPath}",
map_id : #{ajaxMapView.map.id},
onclick : function(d) {
alert(d);
placeClicker([{name: 'placeId', value: d}])
return true;
}
});
// document.getElementById("editbutton").addEventListener("click",
// function() {
// px.enable_edit();
// });
//px.enable_edit();
</script>
</ui:define>
</ui:composition>
</h:body>
</html>
\ No newline at end of file
// d3.tip
// Copyright (c) 2013 Justin Palmer
//
// Tooltips for d3.js SVG visualizations
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module with d3 as a dependency.
define(['d3'], factory)
} else if (typeof module === 'object' && module.exports) {
// CommonJS
module.exports = function(d3) {
d3.tip = factory(d3)
return d3.tip
}
} else {
// Browser global.
root.d3.tip = factory(root.d3)
}
}(this, function (d3) {
// Public - contructs a new tooltip
//
// Returns a tip
return function() {
var direction = d3_tip_direction,
offset = d3_tip_offset,
html = d3_tip_html,
node = initNode(),
svg = null,
point = null,
target = null
function tip(vis) {
svg = getSVGNode(vis)
point = svg.createSVGPoint()
document.body.appendChild(node)
}
// Public - show the tooltip on the screen
//
// Returns a tip
tip.show = function() {
var args = Array.prototype.slice.call(arguments)
if(args[args.length - 1] instanceof SVGElement) target = args.pop()
var content = html.apply(this, args),
poffset = offset.apply(this, args),
dir = direction.apply(this, args),
nodel = d3.select(node),
i = directions.length,
coords,
scrollTop = document.documentElement.scrollTop || document.body.scrollTop,
scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft
nodel.html(content)
.style({ opacity: 1, 'pointer-events': 'all' })
while(i--) nodel.classed(directions[i], false)
coords = direction_callbacks.get(dir).apply(this)
nodel.classed(dir, true).style({
top: (coords.top + poffset[0]) + scrollTop + 'px',
left: (coords.left + poffset[1]) + scrollLeft + 'px'
})
return tip
}
// Public - hide the tooltip
//
// Returns a tip
tip.hide = function() {
var nodel = d3.select(node)
nodel.style({ opacity: 0, 'pointer-events': 'none' })
return tip
}
// Public: Proxy attr calls to the d3 tip container. Sets or gets attribute value.
//
// n - name of the attribute
// v - value of the attribute
//
// Returns tip or attribute value
tip.attr = function(n, v) {
if (arguments.length < 2 && typeof n === 'string') {
return d3.select(node).attr(n)
} else {
var args = Array.prototype.slice.call(arguments)
d3.selection.prototype.attr.apply(d3.select(node), args)
}
return tip
}
// Public: Proxy style calls to the d3 tip container. Sets or gets a style value.
//
// n - name of the property
// v - value of the property
//
// Returns tip or style property value
tip.style = function(n, v) {
if (arguments.length < 2 && typeof n === 'string') {
return d3.select(node).style(n)
} else {
var args = Array.prototype.slice.call(arguments)
d3.selection.prototype.style.apply(d3.select(node), args)
}
return tip
}
// Public: Set or get the direction of the tooltip
//
// v - One of n(north), s(south), e(east), or w(west), nw(northwest),
// sw(southwest), ne(northeast) or se(southeast)
//
// Returns tip or direction
tip.direction = function(v) {
if (!arguments.length) return direction
direction = v == null ? v : d3.functor(v)
return tip
}
// Public: Sets or gets the offset of the tip
//
// v - Array of [x, y] offset
//
// Returns offset or
tip.offset = function(v) {
if (!arguments.length) return offset
offset = v == null ? v : d3.functor(v)
return tip
}
// Public: sets or gets the html value of the tooltip
//
// v - String value of the tip
//
// Returns html value or tip
tip.html = function(v) {
if (!arguments.length) return html
html = v == null ? v : d3.functor(v)
return tip
}
function d3_tip_direction() { return 'n' }
function d3_tip_offset() { return [0, 0] }
function d3_tip_html() { return ' ' }
var direction_callbacks = d3.map({
n: direction_n,
s: direction_s,
e: direction_e,
w: direction_w,
nw: direction_nw,
ne: direction_ne,
sw: direction_sw,
se: direction_se
}),
directions = direction_callbacks.keys()
function direction_n() {
var bbox = getScreenBBox()
return {
top: bbox.n.y - node.offsetHeight,
left: bbox.n.x - node.offsetWidth / 2
}
}
function direction_s() {
var bbox = getScreenBBox()
return {
top: bbox.s.y,
left: bbox.s.x - node.offsetWidth / 2
}
}
function direction_e() {
var bbox = getScreenBBox()
return {
top: bbox.e.y - node.offsetHeight / 2,
left: bbox.e.x
}
}
function direction_w() {
var bbox = getScreenBBox()
return {
top: bbox.w.y - node.offsetHeight / 2,
left: bbox.w.x - node.offsetWidth
}
}
function direction_nw() {
var bbox = getScreenBBox()
return {
top: bbox.nw.y - node.offsetHeight,
left: bbox.nw.x - node.offsetWidth
}
}
function direction_ne() {
var bbox = getScreenBBox()
return {
top: bbox.ne.y - node.offsetHeight,
left: bbox.ne.x
}
}
function direction_sw() {
var bbox = getScreenBBox()
return {
top: bbox.sw.y,
left: bbox.sw.x - node.offsetWidth
}
}
function direction_se() {
var bbox = getScreenBBox()
return {
top: bbox.se.y,
left: bbox.e.x
}
}
function initNode() {
var node = d3.select(document.createElement('div'))
node.style({
position: 'absolute',
top: 0,
opacity: 0,
'pointer-events': 'none',
'box-sizing': 'border-box'
})
return node.node()
}
function getSVGNode(el) {
el = el.node()
if(el.tagName.toLowerCase() === 'svg')
return el
return el.ownerSVGElement
}
// Private - gets the screen coordinates of a shape
//
// Given a shape on the screen, will return an SVGPoint for the directions
// n(north), s(south), e(east), w(west), ne(northeast), se(southeast), nw(northwest),
// sw(southwest).
//
// +-+-+
// | |
// + +
// | |
// +-+-+
//
// Returns an Object {n, s, e, w, nw, sw, ne, se}
function getScreenBBox() {
var targetel = target || d3.event.target,
bbox = {},
matrix = targetel.getScreenCTM(),
tbbox = targetel.getBBox(),
width = tbbox.width,
height = tbbox.height,
x = tbbox.x,
y = tbbox.y
point.x = x
point.y = y
bbox.nw = point.matrixTransform(matrix)
point.x += width
bbox.ne = point.matrixTransform(matrix)
point.y += height
bbox.se = point.matrixTransform(matrix)
point.x -= width
bbox.sw = point.matrixTransform(matrix)
point.y -= height / 2
bbox.w = point.matrixTransform(matrix)
point.x += width
bbox.e = point.matrixTransform(matrix)
point.x -= width / 2
point.y -= height / 2
bbox.n = point.matrixTransform(matrix)
point.y += height
bbox.s = point.matrixTransform(matrix)
return bbox
}
return tip
};
}));
This diff could not be displayed because it is too large.
/*
Placemap css for Moya
*/
.place-tooltip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
/* Creates a small triangle extender for the tooltip */
.place-tooltip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.place-tooltip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
.place-tooltip-upside {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
/*
.place-tooltip-upside:before {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25B2";
position: absolute;
text-align: center;
}
.place-tooltip-upside.n:before {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
*/
.place-edit-tooltip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 10px;
}
.place-edit-tooltip fieldset li {
list-style: none;
padding: 5px;
margin: 0;
}
.place-edit-tooltip fieldset ol {
margin: 0;
padding: 0;
}
.place-edit-tooltip label {
display: inline-block;
line-height: 0.9;
vertical-align: top;
}
.place-edit-tooltip fieldset {
border: none;
margin: 3px 0 0;
}
.place-edit-tooltip label {
width: 70px;
}
.place-edit-tooltip input.numeric {
width: 30px;
}
.place-edit-tooltip fieldset fieldset label {
display: block;
width: auto;
margin-left: 120px;
}
#place_close_popup {
font-size: small;
font-weight: bold;
float: right;
color: #eee;
display: block;
text-decoration: none;
border: 2px solid #666;
padding: 0px 3px 0px 3px;
}
/*
Seatmap javascript library for Moya
*/
function placemap(opts)
{
if ( opts.element === undefined )
{
throw "element is mandatory argument";
}
if ( opts.moyaurl === undefined )
{
throw "moyaurl is mandatory argument";
}
if ( opts.map_id === undefined )
{
throw "map_id is mandatory argument";
}
if (opts.onclick === undefined )
{
throw "onclick is mandatory argument";
}
// px is returned object
var px = {
element: d3.select(opts.element),
moyaurl: opts.moyaurl,
map_id: opts.map_id,
onclick: opts.onclick
};
var guide_layer = map_layer = px.element.append("g").attr("id", "guides");
// Create background
var background = px.element.append("rect")
.attr("class", "background_2")
.attr("width", px.element.attr('width'))
.attr("height", px.element.attr('width'))
.attr("opacity", 0);
// Define layers to use
var map_layer = px.element.append("g").attr("id", "places");
var edit_layer = map_layer = px.element.append("g").attr("id", "edit");
var clicked = false;
px.place_colors = {
"F": "#cccccc",
"L": "#535353",
"R": "#B40100",
"P": "#2FA525",
"T": "#4342C1"
};
px.snap_threshold = {x: 5, y: 5};
var clickpos = {x: 0, y: 0};
var tip = d3.tip()
.attr('class', 'place-tooltip')
.offset([-10, 0])
.html(function(d) {
return "<strong>" + d.name + "</strong>";
});
// TODO: Fix this tip layout
var tip_updown = d3.tip().direction("s")
.attr('class', 'place-tooltip-upside')
.offset([10, 0])
.html(function(d) {
return "<strong>" + d.name + "</strong>";
});
function draw_guides(x1, y1, x2, y2, fade)
{
var guide = guide_layer.append('line')
.attr('x1', x1)
.attr('y1', y1)
.attr('x2', x2)
.attr('y2', y2)
.attr("stroke-width", 1)
.attr("stroke", "#666")
if (fade == true)
{
guide.transition()
.duration(500)
.remove();
}
}
function clear_guides()
{
guide_layer.selectAll('line').remove();
}
function snap_to_edges(x, y, fade)
{
// TODO: if there is node in range, snap coordinates next to it
if (clicked == true) return;
var xmin = x - px.snap_threshold.x;
var xmax = x + px.snap_threshold.x;
var ymin = y - px.snap_threshold.y;
var ymax = y + px.snap_threshold.y;
var xnode, ynode;
var left, top, right, bottom;
map_layer.selectAll('rect').each(function(d) {
left = d.x;
top = d.y;
right = d.x + d.w;
bottom = d.y + d.h;
if (d.x > xmin && d.x < xmax)
{
if (xnode === undefined || Math.abs(x - d.x) < Math.abs(x - xnode.x))
{
x = d.x;
xnode = d;
}
}
if (right > xmin && right < xmax)
{
if (xnode === undefined || Math.abs(x - right) < Math.abs(x - xnode.x - xnode.w))
{
x = right;
xnode = d;
}
}
if (d.y > ymin && d.y < ymax)
{
if (ynode === undefined || Math.abs(y - top) < Math.abs(y - ynode.y)) {
y = d.y;
ynode = d;
}
}
if (bottom > ymin && bottom < ymax)
{
if (ynode === undefined || Math.abs(y - bottom) < Math.abs(y - ynode.y - ynode.h)) {
y = bottom;
ynode = d;
}
}
});
clear_guides();
if (ynode !== undefined)
{
if (ynode.y == y) draw_guides(ynode.x, ynode.y, x, y, fade);
else draw_guides(ynode.x, ynode.y + ynode.h, x, y, fade);
}
if (xnode !== undefined)
{
if (xnode.x == x) draw_guides(xnode.x, xnode.y, x, y, fade);
else draw_guides(xnode.x + xnode.w, xnode.y, x, y, fade);
}
return [x, y]
}
function show_tooltip(d, i) {
var object = {
x: d3.select(this).attr('x'),
y: d3.select(this).attr('y'),
width: d3.select(this).attr('width'),
height: d3.select(this).attr('height')
};
if (object.y >= 40)
{
return tip.show(d, i);
}
else {
return tip_updown.show(d, i);
}
}
function hide_tooltip(d, i)
{
tip.hide(d, i);
tip_updown.hide(d, i);
}
function toggle_clicked(element)
{
// TODO: reserve place from rest first!
data = element.data()[0];
if (px.onclick(data.id) != true) return;
if (data.state == "F")
{
element.transition().duration(100).style("fill", px.place_colors["P"]);
data.state = "P";
}
else if (data.state == "P")
{
element.transition()
.duration(100).style("fill", px.place_colors["F"]);
data.state = "F";
}
}
function place_color(d, i)
{
if (Object.keys(px.place_colors).indexOf(d.state) != -1) return px.place_colors[d.state];
return "#888";
}
function draw_places(data)
{
// TODO: update functionality!
var places = map_layer.selectAll("rect")
.data(data, function(d) { return d.id; }).enter()
.append("rect")
.filter(function(d,i){ return d.state != "D"; })
.attr("x", function(d, i) { return d.x; })
.attr("y", function(d, i) { return d.y; })
.attr("width", function(d, i) { return d.w;})
.attr("height", function(d, i) { return d.h; })
.attr("id", function(d) {return d.id })
.style("fill", place_color)
.style('stroke', '#101010')
.style('stroke-width', '1');
places.append("title", function(d, i) { return d.name; });
if (px.edit_enabled == false)
{
places
.on('mouseover', show_tooltip)
.on('mouseout', hide_tooltip)
.on('click', function (d, i) {
toggle_clicked(d3.select(this));
});
}
}
function edit_clicked(d, i) {
c = d3.mouse(this);
var pos = {x: c[0], y: c[1]};
// Begin click action
if (clicked == false)
{
//coords = snap_to_edges(pos.x, pos.y, true);
clicked = true;
pos.x = coords[0];
pos.y = coords[1];
clickpos = pos;
edit_layer.append("circle")
.attr("cx", pos.x)
.attr("cy", pos.y)
.attr("r", 2)
.attr("width", 10)
.attr("height", 10)
.attr("fill", "#111");
tip_add_places.show(pos.x, pos.y);
}
else {
edit_layer.selectAll("circle").remove();
clicked = false;
tip_add_places.hide(d, i);
}
}
px.edit_enabled = false;
px.enable_edit = function()
{
map_layer.selectAll('rect')
.on('click', null);
background.on("click", edit_clicked);
background.on("mousemove", function(){
c = d3.mouse(this);
var pos = {x: c[0], y: c[1]};
coords = snap_to_edges(pos.x, pos.y, false);
});
px.edit_enabled = true;
};
function gen_row(x,y, width, height, direction, count)
{
var places = [];
place = 1;
for (var i = 0; i < count; i++)
{
places.push({x: x, y: y, w: width, h: height, name: "foo" + i + x + y, id: i*1000+y*100+x, state: "F"});
place += 1;
if (direction == "horizontal")
{
x += width;
}
else {
y += height;
}
}
return places;
}
px.update = function() {
d3.json(px.moyaurl + "/rest/placemap/v1/" + px.map_id + "/places", function(data) {
draw_places(data.places);
});
};
px.init = function() {
d3.json(px.moyaurl + "/rest/placemap/v1/" + px.map_id + "/", function(data){
px.element.attr("id", data.map.id).attr("name", data.map.name);
px.update()
});
};
px.init();
px.element.call(tip);
px.element.call(tip_updown);
// Add tooltip
// TODO: Make this less ugly!
function create_tooltip()
{
var mytip = {};
mytip.node = d3.select(document.createElement('div'));
mytip.offset = [20, 10]
mytip.node.attr("class", 'place-edit-tooltip');
mytip.node.style({
position: 'absolute',
top: 0,
opacity: 0,
'pointer-events': 'none',
'box-sizing': 'border-box'
});
document.body.appendChild(mytip.node.node())
mytip.node.html('<span id="place_close_popup">X</span>'+
'<form><fieldset><ol>'+
'<li><label for="place_add_count">Count:</label><input name="count" id="place_add_count" value=1 class="numeric", min=1, max=999999><span id="place_add_count_error"></span></li>'+
'<li><label for="place_add_width">Width:</label><input name="width" id="place_add_width" value=10 class="numeric", min=1, max=999999> px <span id="place_add_width_error"></span></li>'+
'<li><label for="place_add_height">Height:</label><input name="height" id="place_add_height" value=10 class="numeric", min=1, max=999999> px <span id="place_add_height_error"></span></li>'+
'<li><label for="place_add_direction">Direction:</label>'+
'<select name="place_add_direction" id="place_add_direction"><option value="horizontal">Horizontal &rarr;</option><option value="vertical">Vertical &darr;</option></select></li>'+
'<li><input type="button" name="place_add_rows" id="place_add_rows" value="Add places"></li>'+
'</fieldset></form>');
function number_validator(f, ef) {
return function() {
var field = f;
var errorfield = ef;
var derrorfield = d3.select(ef);
var dfield = d3.select(f);
var min = parseInt(field.min);
var max = parseInt(field.max);
var num = -1;
try {
num = parseInt(field.value)
}
catch(err) {
console.log(err);
}
if (num < min)
{
dfield.style({"border": "2px solid #e22"});
derrorfield.html(" " + field.name + " must be &gt; " + min);
}
else if (num > max)
{
dfield.style({"border": "2px solid #e22"});
derrorfield.html(" " + field.name + " must be &lt; " + max);
}
else if (isNaN(num))
{
dfield.style({"border": "2px solid #e22"});
derrorfield.html(" " + field.name + " must be number");
}
else {
dfield.style({"border": "none"});
derrorfield.html("");
}
}
}
document.getElementById("place_add_count").addEventListener("change",
number_validator(document.getElementById("place_add_count"),
document.getElementById("place_add_count_error")
));
document.getElementById("place_add_width").addEventListener("change",
number_validator(document.getElementById("place_add_width"),
document.getElementById("place_add_width_error")
));
document.getElementById("place_add_height").addEventListener("change",
number_validator(document.getElementById("place_add_height"),
document.getElementById("place_add_height_error")
));
document.getElementById("place_close_popup").addEventListener("click", function(){
edit_layer.selectAll("circle").remove();
clicked = false;
mytip.hide();
});
mytip.show = function (x, y)
{
mytip.node.style ({
top: y + mytip.offset[1] + "px",
left: x + mytip.offset[0] + "px",
opacity: 1,
'pointer-events': 'all'
});
};
mytip.hide = function ()
{
mytip.node.style ({
opacity: 0,
'pointer-events': 'none'
});
};
return mytip;
}
var tip_add_places = create_tooltip();
function add_places(){
var width = parseInt(document.getElementById("place_add_width").value);
var height = parseInt(document.getElementById("place_add_height").value);
var count = parseInt(document.getElementById("place_add_count").value);
var direction = document.getElementById("place_add_direction").value;
draw_places(gen_row(clickpos.x,clickpos.y, width, height, direction, count));
edit_layer.selectAll("circle").remove();
clicked = false;
tip_add_places.hide();
clear_guides();
}
document.getElementById("place_add_rows").addEventListener("click", add_places);
return px;
}
package fi.codecrew.moya.web.cdiview.map;
import java.util.Map;
import javax.ejb.EJB;
import javax.enterprise.context.ConversationScoped;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fi.codecrew.moya.beans.EventBeanLocal;
import fi.codecrew.moya.beans.PlaceBeanLocal;
import fi.codecrew.moya.model.EventMap;
import fi.codecrew.moya.model.LanEvent;
import fi.codecrew.moya.model.Place;
import fi.codecrew.moya.web.cdiview.GenericCDIView;
@Named
@ConversationScoped
public class AjaxMapView extends GenericCDIView {
@Inject
private PlaceView placeview;
private static final Logger logger = LoggerFactory.getLogger(AjaxMapView.class);;
private String testVal = "Testval1";
@EJB
private EventBeanLocal eventbean;
@EJB
private PlaceBeanLocal placebean;
private EventMap map;
private Place place;
public void initMap() {
LanEvent e = eventbean.getCurrentEvent();
setMap(e.getEventMaps().get(0));
}
public void placeClicked()
{
FacesContext context = FacesContext.getCurrentInstance();
Map<String, String> vmap = context.getExternalContext().getRequestParameterMap();
logger.info("placeid {}", vmap.get("pl aceId"));
int placeId = Integer.parseInt(vmap.get("placeId"));
place = placebean.find(placeId);
logger.info("Found place {} with placeid {}", place, placeId);
}
public String getTestVal() {
return testVal;
}
public void setTestVal(String testVal) {
this.testVal = testVal;
}
public EventMap getMap() {
return map;
}
public void setMap(EventMap map) {
this.map = map;
}
public Place getPlace() {
return place;
}
public void setPlace(Place place) {
this.place = place;
}
}
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!