added new map screenshot capture method
This commit is contained in:
parent
d73fa37f57
commit
44923eb660
9 changed files with 197 additions and 26 deletions
BIN
app/assets/images/screenshot_sprite.png
Normal file
BIN
app/assets/images/screenshot_sprite.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
|
@ -20,7 +20,13 @@ Metamaps.JIT = {
|
||||||
|
|
||||||
$(".zoomIn").click(self.zoomIn);
|
$(".zoomIn").click(self.zoomIn);
|
||||||
$(".zoomOut").click(self.zoomOut);
|
$(".zoomOut").click(self.zoomOut);
|
||||||
$(".zoomExtents").click(self.zoomExtents);
|
|
||||||
|
var zoomExtents = function (event) {
|
||||||
|
self.zoomExtents(event, Metamaps.Visualize.mGraph.canvas);
|
||||||
|
};
|
||||||
|
$(".zoomExtents").click(zoomExtents);
|
||||||
|
|
||||||
|
$(".takeScreenshot").click(Metamaps.Map.exportImage);
|
||||||
|
|
||||||
self.synapseStarImage = new Image();
|
self.synapseStarImage = new Image();
|
||||||
self.synapseStarImage.src = '/assets/synapsestar.png';
|
self.synapseStarImage.src = '/assets/synapsestar.png';
|
||||||
|
@ -123,7 +129,11 @@ Metamaps.JIT = {
|
||||||
var directionCat = synapse.get("category");
|
var directionCat = synapse.get("category");
|
||||||
|
|
||||||
//label placement on edges
|
//label placement on edges
|
||||||
Metamaps.JIT.renderEdgeArrows($jit.Graph.Plot.edgeHelper, adj, synapse);
|
if (canvas.denySelected) {
|
||||||
|
var color = Metamaps.Settings.colors.synapses.normal;
|
||||||
|
canvas.getCtx().fillStyle = canvas.getCtx().strokeStyle = color;
|
||||||
|
}
|
||||||
|
Metamaps.JIT.renderEdgeArrows($jit.Graph.Plot.edgeHelper, adj, synapse, canvas);
|
||||||
|
|
||||||
//check for edge label in data
|
//check for edge label in data
|
||||||
var desc = synapse.get("desc");
|
var desc = synapse.get("desc");
|
||||||
|
@ -140,7 +150,7 @@ Metamaps.JIT = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (desc != "" && showDesc) {
|
if (!canvas.denySelected && desc != "" && showDesc) {
|
||||||
// '&' to '&'
|
// '&' to '&'
|
||||||
desc = Metamaps.Util.decodeEntities(desc);
|
desc = Metamaps.Util.decodeEntities(desc);
|
||||||
|
|
||||||
|
@ -188,7 +198,7 @@ Metamaps.JIT = {
|
||||||
drawStar(ctx, x + width, y);
|
drawStar(ctx, x + width, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (showDesc) {
|
else if (!canvas.denySelected && showDesc) {
|
||||||
if (adj.getData("synapses").length > 1) {
|
if (adj.getData("synapses").length > 1) {
|
||||||
var ctx = canvas.getCtx();
|
var ctx = canvas.getCtx();
|
||||||
var x = (pos.x + posChild.x) / 2;
|
var x = (pos.x + posChild.x) / 2;
|
||||||
|
@ -400,7 +410,7 @@ Metamaps.JIT = {
|
||||||
ctx = canvas.getCtx();
|
ctx = canvas.getCtx();
|
||||||
|
|
||||||
// if the topic is selected draw a circle around it
|
// if the topic is selected draw a circle around it
|
||||||
if (node.selected) {
|
if (!canvas.denySelected && node.selected) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(pos.x, pos.y, dim + 3, 0, 2 * Math.PI, false);
|
ctx.arc(pos.x, pos.y, dim + 3, 0, 2 * Math.PI, false);
|
||||||
ctx.strokeStyle = Metamaps.Settings.colors.topics.selected;
|
ctx.strokeStyle = Metamaps.Settings.colors.topics.selected;
|
||||||
|
@ -1595,12 +1605,10 @@ Metamaps.JIT = {
|
||||||
ctx.lineTo(v2.x, v2.y);
|
ctx.lineTo(v2.x, v2.y);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}, // renderMidArrow
|
}, // renderMidArrow
|
||||||
renderEdgeArrows: function (edgeHelper, adj, synapse) {
|
renderEdgeArrows: function (edgeHelper, adj, synapse, canvas) {
|
||||||
|
|
||||||
var self = Metamaps.JIT;
|
var self = Metamaps.JIT;
|
||||||
|
|
||||||
var canvas = Metamaps.Visualize.mGraph.canvas;
|
|
||||||
|
|
||||||
var directionCat = synapse.get('category');
|
var directionCat = synapse.get('category');
|
||||||
var direction = synapse.getDirection();
|
var direction = synapse.getDirection();
|
||||||
|
|
||||||
|
@ -1657,8 +1665,7 @@ Metamaps.JIT = {
|
||||||
Metamaps.Visualize.mGraph.canvas.scale(0.8,0.8);
|
Metamaps.Visualize.mGraph.canvas.scale(0.8,0.8);
|
||||||
$(document).trigger(Metamaps.JIT.events.zoom, [event]);
|
$(document).trigger(Metamaps.JIT.events.zoom, [event]);
|
||||||
},
|
},
|
||||||
centerMap: function () {
|
centerMap: function (canvas) {
|
||||||
var canvas = Metamaps.Visualize.mGraph.canvas;
|
|
||||||
var offsetScale = canvas.scaleOffsetX;
|
var offsetScale = canvas.scaleOffsetX;
|
||||||
|
|
||||||
canvas.scale(1/offsetScale,1/offsetScale);
|
canvas.scale(1/offsetScale,1/offsetScale);
|
||||||
|
@ -1674,7 +1681,8 @@ Metamaps.JIT = {
|
||||||
eX = Metamaps.Mouse.boxEndCoordinates.x,
|
eX = Metamaps.Mouse.boxEndCoordinates.x,
|
||||||
eY = Metamaps.Mouse.boxEndCoordinates.y;
|
eY = Metamaps.Mouse.boxEndCoordinates.y;
|
||||||
|
|
||||||
Metamaps.JIT.centerMap();
|
var canvas = Metamaps.Visualize.mGraph.canvas;
|
||||||
|
Metamaps.JIT.centerMap(canvas);
|
||||||
|
|
||||||
var height = $(document).height(),
|
var height = $(document).height(),
|
||||||
width = $(document).width();
|
width = $(document).width();
|
||||||
|
@ -1686,8 +1694,6 @@ Metamaps.JIT = {
|
||||||
|
|
||||||
var newRatio = Math.min(ratioX,ratioY);
|
var newRatio = Math.min(ratioX,ratioY);
|
||||||
|
|
||||||
var canvas = Metamaps.Visualize.mGraph.canvas;
|
|
||||||
|
|
||||||
if(canvas.scaleOffsetX *newRatio<= 5 && canvas.scaleOffsetX*newRatio >= 0.2){
|
if(canvas.scaleOffsetX *newRatio<= 5 && canvas.scaleOffsetX*newRatio >= 0.2){
|
||||||
canvas.scale(newRatio,newRatio);
|
canvas.scale(newRatio,newRatio);
|
||||||
}
|
}
|
||||||
|
@ -1711,15 +1717,14 @@ Metamaps.JIT = {
|
||||||
Metamaps.Visualize.mGraph.plot();
|
Metamaps.Visualize.mGraph.plot();
|
||||||
|
|
||||||
},
|
},
|
||||||
zoomExtents: function (event) {
|
zoomExtents: function (event, canvas, denySelected) {
|
||||||
Metamaps.JIT.centerMap();
|
Metamaps.JIT.centerMap(canvas);
|
||||||
var height = $(document).height(),
|
var height = canvas.getSize().height,
|
||||||
width = $(document).width(),
|
width = canvas.getSize().width,
|
||||||
maxX, minX, maxY, minY, counter = 0;
|
maxX, minX, maxY, minY, counter = 0;
|
||||||
var canvas = Metamaps.Visualize.mGraph.canvas;
|
|
||||||
|
|
||||||
|
|
||||||
if (Metamaps.Selected.Nodes.length > 0) {
|
if (!denySelected && Metamaps.Selected.Nodes.length > 0) {
|
||||||
var nodes = Metamaps.Selected.Nodes;
|
var nodes = Metamaps.Selected.Nodes;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -125,7 +125,7 @@
|
||||||
if (Metamaps.Visualize.mGraph) {
|
if (Metamaps.Visualize.mGraph) {
|
||||||
Metamaps.Visualize.mGraph.graph.empty();
|
Metamaps.Visualize.mGraph.graph.empty();
|
||||||
Metamaps.Visualize.mGraph.plot();
|
Metamaps.Visualize.mGraph.plot();
|
||||||
Metamaps.JIT.centerMap();
|
Metamaps.JIT.centerMap(Metamaps.Visualize.mGraph.canvas);
|
||||||
}
|
}
|
||||||
Metamaps.Famous.viz.show();
|
Metamaps.Famous.viz.show();
|
||||||
Metamaps.Topic.end();
|
Metamaps.Topic.end();
|
||||||
|
@ -156,7 +156,7 @@
|
||||||
if (Metamaps.Visualize.mGraph) {
|
if (Metamaps.Visualize.mGraph) {
|
||||||
Metamaps.Visualize.mGraph.graph.empty();
|
Metamaps.Visualize.mGraph.graph.empty();
|
||||||
Metamaps.Visualize.mGraph.plot();
|
Metamaps.Visualize.mGraph.plot();
|
||||||
Metamaps.JIT.centerMap();
|
Metamaps.JIT.centerMap(Metamaps.Visualize.mGraph.canvas);
|
||||||
}
|
}
|
||||||
Metamaps.Famous.viz.show();
|
Metamaps.Famous.viz.show();
|
||||||
Metamaps.Map.end();
|
Metamaps.Map.end();
|
||||||
|
|
|
@ -3384,7 +3384,7 @@ Metamaps.Listeners = {
|
||||||
case 69: //if e or E is pressed
|
case 69: //if e or E is pressed
|
||||||
if (e.ctrlKey){
|
if (e.ctrlKey){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Metamaps.JIT.zoomExtents();
|
Metamaps.JIT.zoomExtents(null, Metamaps.Visualize.mGraph.canvas);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 77: //if m or M is pressed
|
case 77: //if m or M is pressed
|
||||||
|
@ -4203,6 +4203,130 @@ Metamaps.Map = {
|
||||||
Metamaps.Map.sideLength = 1;
|
Metamaps.Map.sideLength = 1;
|
||||||
Metamaps.Map.timeToTurn = 0;
|
Metamaps.Map.timeToTurn = 0;
|
||||||
Metamaps.Map.turnCount = 0;
|
Metamaps.Map.turnCount = 0;
|
||||||
|
},
|
||||||
|
exportImage: function() {
|
||||||
|
|
||||||
|
var canvas = {};
|
||||||
|
|
||||||
|
canvas.canvas = document.createElement("canvas");
|
||||||
|
canvas.canvas.width = 1880; // 960;
|
||||||
|
canvas.canvas.height = 1260; // 630
|
||||||
|
|
||||||
|
canvas.scaleOffsetX = 1;
|
||||||
|
canvas.scaleOffsetY = 1;
|
||||||
|
canvas.translateOffsetY = 0;
|
||||||
|
canvas.translateOffsetX = 0;
|
||||||
|
canvas.denySelected = true;
|
||||||
|
|
||||||
|
canvas.getSize = function() {
|
||||||
|
if(this.size) return this.size;
|
||||||
|
var canvas = this.canvas;
|
||||||
|
return this.size = {
|
||||||
|
width: canvas.width,
|
||||||
|
height: canvas.height
|
||||||
|
};
|
||||||
|
};
|
||||||
|
canvas.scale = function(x, y) {
|
||||||
|
var px = this.scaleOffsetX * x,
|
||||||
|
py = this.scaleOffsetY * y;
|
||||||
|
var dx = this.translateOffsetX * (x -1) / px,
|
||||||
|
dy = this.translateOffsetY * (y -1) / py;
|
||||||
|
this.scaleOffsetX = px;
|
||||||
|
this.scaleOffsetY = py;
|
||||||
|
this.getCtx().scale(x, y);
|
||||||
|
this.translate(dx, dy);
|
||||||
|
};
|
||||||
|
canvas.translate = function(x, y) {
|
||||||
|
var sx = this.scaleOffsetX,
|
||||||
|
sy = this.scaleOffsetY;
|
||||||
|
this.translateOffsetX += x*sx;
|
||||||
|
this.translateOffsetY += y*sy;
|
||||||
|
this.getCtx().translate(x, y);
|
||||||
|
};
|
||||||
|
canvas.getCtx = function() {
|
||||||
|
return this.canvas.getContext("2d");
|
||||||
|
};
|
||||||
|
// center it
|
||||||
|
canvas.getCtx().translate(1880/2, 1260/2);
|
||||||
|
|
||||||
|
var mGraph = Metamaps.Visualize.mGraph;
|
||||||
|
|
||||||
|
var id = mGraph.root;
|
||||||
|
var root = mGraph.graph.getNode(id);
|
||||||
|
var T = !!root.visited;
|
||||||
|
|
||||||
|
// pass true to avoid basing it on a selection
|
||||||
|
Metamaps.JIT.zoomExtents(null, canvas, true);
|
||||||
|
|
||||||
|
var c = canvas.canvas,
|
||||||
|
ctx = canvas.getCtx(),
|
||||||
|
scale = canvas.scaleOffsetX;
|
||||||
|
|
||||||
|
// draw a grey background
|
||||||
|
ctx.fillStyle = '#d8d9da';
|
||||||
|
var xPoint = (-(c.width/scale)/2) - (canvas.translateOffsetX/scale),
|
||||||
|
yPoint = (-(c.height/scale)/2) - (canvas.translateOffsetY/scale);
|
||||||
|
ctx.fillRect(xPoint,yPoint,c.width/scale,c.height/scale);
|
||||||
|
|
||||||
|
// draw the graph
|
||||||
|
mGraph.graph.eachNode(function(node) {
|
||||||
|
var nodeAlpha = node.getData('alpha');
|
||||||
|
node.eachAdjacency(function(adj) {
|
||||||
|
var nodeTo = adj.nodeTo;
|
||||||
|
if(!!nodeTo.visited === T && node.drawn && nodeTo.drawn) {
|
||||||
|
mGraph.fx.plotLine(adj, canvas);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if(node.drawn) {
|
||||||
|
mGraph.fx.plotNode(node, canvas);
|
||||||
|
}
|
||||||
|
if(!mGraph.labelsHidden) {
|
||||||
|
if(node.drawn && nodeAlpha >= 0.95) {
|
||||||
|
mGraph.labels.plotLabel(canvas, node);
|
||||||
|
} else {
|
||||||
|
mGraph.labels.hideLabel(node, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.visited = !T;
|
||||||
|
});
|
||||||
|
|
||||||
|
var imageData = {
|
||||||
|
encoded_image: canvas.canvas.toDataURL()
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(imageData.encoded_image);
|
||||||
|
var map = Metamaps.Active.Map;
|
||||||
|
|
||||||
|
var today = new Date();
|
||||||
|
var dd = today.getDate();
|
||||||
|
var mm = today.getMonth()+1; //January is 0!
|
||||||
|
var yyyy = today.getFullYear();
|
||||||
|
if(dd<10) {
|
||||||
|
dd='0'+dd
|
||||||
|
}
|
||||||
|
if(mm<10) {
|
||||||
|
mm='0'+mm
|
||||||
|
}
|
||||||
|
today = mm+'/'+dd+'/'+yyyy;
|
||||||
|
|
||||||
|
var downloadMessage = "";
|
||||||
|
downloadMessage += "Captured map screenshot! ";
|
||||||
|
downloadMessage += "<a href='" + imageData.encoded_image + "' ";
|
||||||
|
downloadMessage += "download='map-" + map.id + "-screenshot-" + today + ".png'>DOWNLOAD</a>";
|
||||||
|
Metamaps.GlobalUI.notifyUser(downloadMessage);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
dataType: 'json',
|
||||||
|
url: "/maps/" + Metamaps.Active.Map.id + "/upload_screenshot",
|
||||||
|
data: imageData,
|
||||||
|
success: function (data) {
|
||||||
|
console.log('successfully uploaded map screenshot');
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
console.log('failed to save map screenshot');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -763,6 +763,18 @@
|
||||||
background-position: 0 0;
|
background-position: 0 0;
|
||||||
cursor:pointer;
|
cursor:pointer;
|
||||||
}
|
}
|
||||||
|
.takeScreenshot {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background-image: url(screenshot_sprite.png);
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.takeScreenshot:hover {
|
||||||
|
background-position: -32px 0;
|
||||||
|
}
|
||||||
|
.canEditMap .takeScreenshot {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
.zoomExtents {
|
.zoomExtents {
|
||||||
margin-bottom:5px;
|
margin-bottom:5px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
@ -883,6 +895,10 @@
|
||||||
line-height:14px;
|
line-height:14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toast a {
|
||||||
|
color: #4fc059;
|
||||||
|
}
|
||||||
|
|
||||||
/* end toast */
|
/* end toast */
|
||||||
|
|
||||||
/* feedback */
|
/* feedback */
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class MapsController < ApplicationController
|
class MapsController < ApplicationController
|
||||||
|
|
||||||
before_filter :require_user, only: [:create, :update, :destroy]
|
before_filter :require_user, only: [:create, :update, :screenshot, :destroy]
|
||||||
|
|
||||||
respond_to :html, :json
|
respond_to :html, :json
|
||||||
|
|
||||||
|
@ -183,6 +183,30 @@ class MapsController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# POST maps/:id/upload_screenshot
|
||||||
|
def screenshot
|
||||||
|
@current = current_user
|
||||||
|
@map = Map.find(params[:id]).authorize_to_edit(@current)
|
||||||
|
|
||||||
|
if @map
|
||||||
|
png = Base64.decode64(params[:encoded_image]['data:image/png;base64,'.length .. -1])
|
||||||
|
StringIO.open(png) do |data|
|
||||||
|
data.class.class_eval { attr_accessor :original_filename, :content_type }
|
||||||
|
data.original_filename = "map-" + @map.id.to_s + "-screenshot.png"
|
||||||
|
data.content_type = "image/png"
|
||||||
|
@map.screenshot = data
|
||||||
|
end
|
||||||
|
|
||||||
|
if @map.save
|
||||||
|
render :json => {:message => "Successfully uploaded the map screenshot."}
|
||||||
|
else
|
||||||
|
render :json => {:message => "Failed to upload image."}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
render :json => {:message => "Unauthorized to set map screenshot."}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# DELETE maps/:id
|
# DELETE maps/:id
|
||||||
def destroy
|
def destroy
|
||||||
@current = current_user
|
@current = current_user
|
||||||
|
|
|
@ -8,12 +8,12 @@ class Map < ActiveRecord::Base
|
||||||
has_many :topics, :through => :topicmappings
|
has_many :topics, :through => :topicmappings
|
||||||
has_many :synapses, :through => :synapsemappings
|
has_many :synapses, :through => :synapsemappings
|
||||||
|
|
||||||
after_touch :save_screenshot
|
#after_touch :save_screenshot
|
||||||
|
|
||||||
# This method associates the attribute ":image" with a file attachment
|
# This method associates the attribute ":image" with a file attachment
|
||||||
has_attached_file :screenshot, :styles => {
|
has_attached_file :screenshot, :styles => {
|
||||||
:thumb => ['188x126#', :png],
|
:thumb => ['188x126#', :png]
|
||||||
:full => ['940x630#', :png]
|
#:full => ['940x630#', :png]
|
||||||
},
|
},
|
||||||
:default_url => "/assets/missing-map.png"
|
:default_url => "/assets/missing-map.png"
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<div class="mapControls mapElement">
|
<div class="mapControls mapElement">
|
||||||
|
<div class="takeScreenshot mapControl"></div>
|
||||||
<div class="zoomExtents mapControl"></div>
|
<div class="zoomExtents mapControl"></div>
|
||||||
<div class="zoomIn mapControl"></div>
|
<div class="zoomIn mapControl"></div>
|
||||||
<div class="zoomOut mapControl"></div>
|
<div class="zoomOut mapControl"></div>
|
||||||
|
|
|
@ -30,6 +30,7 @@ Metamaps::Application.routes.draw do
|
||||||
match 'maps/topics/:id', to: 'maps#index', via: :get, as: :topicmaps
|
match 'maps/topics/:id', to: 'maps#index', via: :get, as: :topicmaps
|
||||||
resources :maps, except: [:new, :edit]
|
resources :maps, except: [:new, :edit]
|
||||||
match 'maps/:id/contains', to: 'maps#contains', via: :get, as: :contains
|
match 'maps/:id/contains', to: 'maps#contains', via: :get, as: :contains
|
||||||
|
match 'maps/:id/upload_screenshot', to: 'maps#screenshot', via: :post, as: :screenshot
|
||||||
|
|
||||||
devise_for :users, controllers: { registrations: 'users/registrations', passwords: 'users/passwords', sessions: 'devise/sessions' }, :skip => [:sessions]
|
devise_for :users, controllers: { registrations: 'users/registrations', passwords: 'users/passwords', sessions: 'devise/sessions' }, :skip => [:sessions]
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue