I have tested this on an iPad (3 generation). The line chart appears perfectly smooth.
http://justindarc.github.com/flot.touch/example/index.html
I have tried diffing the code with my flot code and cannot see any significant difference that would cause it to run in retina mode. My flot is version 0.7, same as his code. No matter what I try my own chart runs in non-retina mode.
What is the trick in running retina mode?
My setup code is rather long.
function setup_chart0(setup_options) {
var point_data = [];
if(setup_options.use_sample_data_for_chart0) {
point_data = generate_dummy_data(
setup_options.timestamp_generate_min,
setup_options.timestamp_generate_max
);
}
var average_data = henderson23(point_data);
var datasets = make_chart0_datasets(point_data, average_data);
if(is_dualaxis_detail_mode) {
datasets = make_chart0_dual_datasets(
[],
[],
[],
[]
);
}
var options = default_plot_options();
options.xaxis.min = setup_options.timestamp_visible_min;
options.xaxis.max = setup_options.timestamp_visible_max;
options.xaxis.panRange = [setup_options.timestamp_pan_min, setup_options.timestamp_pan_max];
options.yaxis.min = -0.025;
options.yaxis.max = 1.025;
options.yaxis.panRange = [-0.025, 1.025];
options.legend = { container: '#legend0', noColumns: 2 };
options.grid.markings = compute_markings_with_alertlevels;
if(is_dualaxis_detail_mode) {
options.y2axis = {};
options.y2axis.position = "right";
options.y2axis.min = -0.025;
options.y2axis.max = 1.025;
options.y2axis.panRange = [-0.025, 1.025];
options.legend = { container: '#legend_hidden', noColumns: 2 };
}
//save_as_file({ samples: point_data, average: average_data });
var el_placeholder0 = $("#placeholder0");
if(el_placeholder0.length){
//console.log('plotting chart 0');
var fade = false;
var el = el_placeholder0;
var el_outer = $("#placeholder0_outer");
var original_offset = el_outer.offset();
if(fade) {
el_outer.offset({ top: -5000, left: 0 }); // move plot container off screen
}
chart0 = $.plot(el, datasets, options);
if(fade) {
el.hide(); // hide plot - must do *after* creation
el_outer.offset(original_offset); // put plot back where it belongs
el.fadeIn('slow'); // fade in
}
/*var s = ' width: ' + chart0.width() + ' height: ' + chart0.height();
$('#label0').append(s);*/
if(solo_pan_mode) {
el.bind('plotpan', function (event, plot) {
set_data_should_redraw_chart0 = true;
set_data_should_redraw_chart1 = false;
set_data_should_redraw_chart2 = false;
fetch_data_for_chart(chart0, setup_options.timestamp);
show_loading_empty('#loader1');
show_loading_empty('#loader2');
});
el.bind('plotpanend', function (event, plot) {
set_data_should_redraw_chart0 = true;
set_data_should_redraw_chart1 = true;
set_data_should_redraw_chart2 = true;
copy_min_max(chart0, chart1, '#placeholder1');
copy_min_max(chart0, chart2, '#placeholder2');
hack_hide_loading_wheels = true;
maybe_hide_loading_wheels();
});
} else {
el.bind('plotpan', function (event, plot) {
fetch_data_for_chart(chart0, setup_options.timestamp);
sync_with_chart0();
});
}
}
}
I have modified jquery.flot.js like this:
In the top I have added
retinaMode = (window.devicePixelRatio > 1),
I have extended these functions
function makeCanvas(skipPositioning, cls) {
var c = document.createElement('canvas');
c.className = cls;
c.width = canvasWidth;
c.height = canvasHeight;
if (!skipPositioning)
$(c).css({ position: 'absolute', left: 0, top: 0 });
$(c).appendTo(placeholder);
if(retinaMode) {
c.width = canvasWidth * 2;
c.height = canvasHeight * 2;
c.style.width = '' + canvasWidth + 'px';
c.style.height = '' + canvasHeight + 'px';
}
if (!c.getContext) // excanvas hack
c = window.G_vmlCanvasManager.initElement(c);
// used for resetting in case we get replotted
c.getContext("2d").save();
if (retinaMode) {
c.getContext("2d").scale(2,2);
}
return c;
}
function getCanvasDimensions() {
canvasWidth = placeholder.width();
canvasHeight = placeholder.height();
if (canvasWidth <= 0 || canvasHeight <= 0)
throw "Invalid dimensions for plot, width = " + canvasWidth + ", height = " + canvasHeight;
}
function resizeCanvas(c) {
// resizing should reset the state (excanvas seems to be
// buggy though)
if (c.width != canvasWidth) {
c.width = canvasWidth;
if(retinaMode) {
c.width = canvasWidth * 2;
}
c.style.width = '' + canvasWidth + 'px';
}
if (c.height != canvasHeight) {
c.height = canvasHeight;
if(retinaMode) {
c.height = canvasHeight * 2;
}
c.style.height = '' + canvasHeight + 'px';
}
// so try to get back to the initial state (even if it's
// gone now, this should be safe according to the spec)
var cctx = c.getContext("2d");
cctx.restore();
// and save again
cctx.save();
if(retinaMode) {
cctx.scale(2, 2);
}
}
Related
I'am looking a good ideas to build a cacsade forms.
what Iam looking for is to build a form to construct a config yml data.
for example to build key value form and the value field can be string or hash or array.
config --> Items --> has key and value --> can be String, Array or Hash.
Any Idea please?
what i did is to generate a graphe using 3djs and make editabe a node and handle add/ remove a node. I share whith you what I did for persons want do the same thing... (flare.json is the action to retrive json file )
var root;
treeJSON = d3.json("flare.json", function(error, treeData) {
// Calculate total nodes, max label length
var totalNodes = 0;
var maxLabelLength = 0;
// variables for drag/drop
var selectedNode = null;
var draggingNode = null;
// panning variables
var panSpeed = 200;
var panBoundary = 20; // Within 20px from edges will pan when dragging.
// Misc. variables
var i = 0;
var duration = 750;
// var root;
// size of the diagram
var viewerWidth = $('#d3view').width();
var viewerHeight = 800;
var tree = d3.layout.tree()
.size([viewerHeight, viewerWidth]);
// define a d3 diagonal projection for use by the node paths later on.
var diagonal = d3.svg.diagonal()
.projection(function(d) {
return [d.y, d.x];
});
// A recursive helper function for performing some setup by walking through all nodes
function visit(parent, visitFn, childrenFn) {
if (!parent) return;
visitFn(parent);
var children = childrenFn(parent);
if (children) {
var count = children.length;
for (var i = 0; i < count; i++) {
visit(children[i], visitFn, childrenFn);
}
}
}
// Call visit function to establish maxLabelLength
visit(treeData, function(d) {
totalNodes++;
maxLabelLength = Math.max(d.name.length, maxLabelLength);
}, function(d) {
return d.children && d.children.length > 0 ? d.children : null;
});
// sort the tree according to the node names
function sortTree() {
tree.sort(function(a, b) {
return b.name.toLowerCase() < a.name.toLowerCase() ? 1 : -1;
});
}
// Sort the tree initially incase the JSON isn't in a sorted order.
sortTree();
// Define the zoom function for the zoomable tree
function zoom() {
$('.btn-form').remove()
svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
// define the zoomListener which calls the zoom function on the "zoom" event constrained within the scaleExtents
var zoomListener = d3.behavior.zoom().scaleExtent([0.1, 1.5]).on("zoom", zoom);
// define the baseSvg, attaching a class for styling and the zoomListener
var baseSvg = d3.select("#d3view").append("svg")
.attr("width", viewerWidth)
.attr("height", viewerHeight)
.attr("class", "overlay")
.call(zoomListener);
// Function to center node when clicked/dropped so node doesn't get lost when collapsing/moving with large amount of children.
function centerNode(source) {
scale = 0.3;//zoomListener.scale();
x = -source.y0;
y = -source.x0;
x = x * scale + viewerWidth / 20;
y = y * scale + viewerHeight / 2.5;
d3.select('g').transition()
.duration(duration)
.attr("transform", "translate(" + x + "," + y + ")scale(" + scale + ")");
zoomListener.scale(scale);
zoomListener.translate([x, y]);
}
// Toggle children function
function toggleChildren(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else if (d._children) {
d.children = d._children;
d._children = null;
}
return d;
}
// Toggle children on click.
function click(d) {
if (d3.event.defaultPrevented) return; // click suppressed
d = toggleChildren(d);
update(d);
// centerNode(d);
}
function remove_link(d){
a = d3.event
a.preventDefault()
p = d.parent
parent_array = [p]
depth = d.depth - 1
for(var i=1; i<depth; i++){
parent_1 = p
parent_2 = parent_1.parent
parent_array.push(parent_2)
p = parent_2
}
parent_array[0].children.splice($.inArray(d, parent_array[0].children), 1)
update(d);
$('#editme').tree('loadData', [root]);
}
function make_editable(d)
{
// console.log("make_editable", arguments);
this
.on("mouseover", function() {
$('.btn-form').remove();
$('.input-form').remove();
d3.selectAll('text').style('fill', null)
d3.select(this).style("fill", "red");
var p = this.parentNode;
var xy = this.getBBox();
var p_xy = p.getBBox();
xy.x -= p_xy.x;
xy.y -= p_xy.y;
xy.x += 25;
xy.y -= 50;
var el = d3.select(this);
var p_el = d3.select(p);
var frm = p_el.append("foreignObject");
var inp = frm
.attr('class', 'btn-form')
.attr("x", xy.x)
.attr("y", xy.y)
.attr("width", 145)
.attr("height", 60)
.append("xhtml:form")
.append('a').text('Add').attr('class', 'btn btn-primary').attr('style', 'width:50px;height:30px;margin-right:2%;').on('click', add_link)
frm.select('form').append('a').text('Remove').attr('class', 'btn btn-danger remove-btn').attr('style', 'width:70px;height:30px;').on('click', remove_link)
frm.on('mouseover', function(){
$('.btn-form').closest('g').find('text').attr("fill", 'red');
})
.on('mouseout', function(){
$('.btn-form').closest('g').find('text').attr("fill", null);
})
})
.on("mouseout", function() {
d3.select(this).style("fill", null);
})
.on("click", function(d, event) {
e = d3.event;
e.preventDefault();
$('.btn-form').remove();
var p = this.parentNode;
// console.log(this, arguments);
// inject a HTML form to edit the content here...
// bug in the getBBox logic here, but don't know what I've done wrong here;
// anyhow, the coordinates are completely off & wrong. :-((
var xy = this.getBBox();
var p_xy = p.getBBox();
xy.x -= p_xy.x;
xy.y -= p_xy.y;
var el = d3.select(this);
var p_el = d3.select(p);
var frm = p_el.append("foreignObject");
var inp = frm
.attr('class', 'input-form')
.attr("x", xy.x)
.attr("y", xy.y)
.style("z-index", 99999)
.attr("width", 300)
.attr("height", 25)
.append("xhtml:form")
.append("input")
.attr("value", function() {
// nasty spot to place this call, but here we are sure that the <input> tag is available
// and is handily pointed at by 'this':
this.focus();
return el.text();
})
.attr("style", "width: 110px;")
// make the form go away when you jump out (form looses focus) or hit ENTER:
.on("blur", function() {
// console.log("blur", this, arguments);
var txt = inp.node().value;
el.text(function(d) { return txt; });
d.name = el.text();
// Note to self: frm.remove() will remove the entire <g> group! Remember the D3 selection logic!
p_el.select("foreignObject").remove();
})
.on("keypress", function() {
// console.log("keypress", this, arguments);
// IE fix
if (!d3.event)
d3.event = window.event;
var e = d3.event;
if (e.keyCode == 13)
{
if (typeof(e.cancelBubble) !== 'undefined') // IE
e.cancelBubble = true;
if (e.stopPropagation)
e.stopPropagation();
e.preventDefault();
var txt = inp.node().value;
el.text(function(d) { return txt; });
p_el.attr('data-name', txt);
d.name = el.text();
$('#editme').tree('loadData', [root]);
}
});
});
}
function add_link(d){
d3.event.preventDefault();
var p = $(this).closest('form')
var xy = $(this).closest('.btn-form')[0].getBBox();
if($(".add-link").length == 0){
var frm = p.append("<div><input type='text' class='add-link' style='width:130px;'></input></div>");
var inp = frm.on('keypress', function(event){
var e = event;
if (e.keyCode == 13)
{
e.preventDefault();
var txt = $('.add-link').val();
var event = document.createEvent('Event');
event.initEvent('click', true, true);
if(d.children){
d.children.push({name: txt})
}else if(d._children){
d._children.push({name: txt})
}else{
d.children = [{name: txt}]
}
$('text[fill="red"]').attr('fill', null);
update(root);
$(".btn-form").hide();
$('#editme').tree('loadData', [root]);
}
})
.on('click', function(event){ event.preventDefault();})
$('.add-link').focus()
}
}
function update(source) {
// Compute the new height, function counts total children of root node and sets tree height accordingly.
// This prevents the layout looking squashed when new nodes are made visible or looking sparse when nodes are removed
// This makes the layout more consistent.
var levelWidth = [1];
var childCount = function(level, n) {
if (n.children && n.children.length > 0) {
if (levelWidth.length <= level + 1) levelWidth.push(0);
levelWidth[level + 1] += n.children.length;
n.children.forEach(function(d) {
childCount(level + 1, d);
});
}
};
childCount(0, root);
var newHeight = d3.max(levelWidth) * 25; // 25 pixels per line
tree = tree.size([newHeight, viewerWidth]);
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Set widths between levels based on maxLabelLength.
nodes.forEach(function(d) {
d.y = (d.depth * (maxLabelLength * 10)); //maxLabelLength * 10px
// alternatively to keep a fixed scale one can set a fixed depth per level
// Normalize for fixed-depth by commenting out below line
// d.y = (d.depth * 500); //500px per level.
});
// Update the nodes…
node = svgGroup.selectAll("g.node")
.data(nodes, function(d) {
return d.id || (d.id = ++i);
});
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
// .call(dragListener)
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
.on('click', click);
nodeEnter.append("circle")
.attr('class', 'nodeCircle')
.attr("r", 0)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeEnter.append("text")
.attr("x", function(d) {
return d.children || d._children ? -10 : 10;
})
.attr("dy", ".35em")
.attr('class', 'nodeText')
.attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
})
.text(function(d) {
return d.name;
})
.style("fill-opacity", 0)
.call(make_editable);
// phantom node to give us mouseover in a radius around it
// nodeEnter.append("circle")
// .attr('class', 'ghostCircle')
// .attr("r", 30)
// .attr("opacity", 0.2) // change this to zero to hide the target area
// .style("fill", "red")
// .attr('pointer-events', 'mouseover');
// .on("mouseover", function(node) {
// overCircle(node);
// })
// .on("mouseout", function(node) {
// outCircle(node);
// });
// Update the text to reflect whether node has children or not.
node.select('text')
.attr("x", function(d) {
return d.children || d._children ? -10 : 10;
})
.attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
})
.text(function(d) {
return d.name;
});
// Change the circle fill depending on whether it has children and is collapsed
node.select("circle.nodeCircle")
.attr("r", 4.5)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
});
// Fade the text in
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + source.y + "," + source.x + ")";
})
.remove();
nodeExit.select("circle")
.attr("r", 0);
nodeExit.select("text")
.style("fill-opacity", 0);
// Update the links…
var link = svgGroup.selectAll("path.link")
.data(links, function(d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {
x: source.x0,
y: source.y0
};
return diagonal({
source: o,
target: o
});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {
x: source.x,
y: source.y
};
return diagonal({
source: o,
target: o
});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
function expand(d) {
if (d._children) {
d.children = d._children;
d.children.forEach(expand);
d._children = null;
}else if(d.children) {
d.children.forEach(expand);
}
}
// Append a group which holds all nodes and which the zoom Listener can act upon.
var svgGroup = baseSvg.append("g");
// Define the root
root = treeData;
root.x0 = viewerHeight / 2;
root.y0 = 0;
// Layout the tree initially and center on the root node.
update(root);
centerNode(root);
});
(function() {
'use strict';
//
// Utilities
//
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
}
function now() {
return Math.floor(Date.now() / 1000);
}
//
// Environment
//
var myUuid;
var mapId;
mapId = location.hash.replace(/^#/, '');
if (!mapId) {
mapId = (Math.random() + 1).toString(36).substring(2, 12);
location.hash = mapId;
}
if (typeof localStorage == 'undefined') {
document.getElementById('map').innerHTML = 'Sorry but your browser is not supported'
return
}
myUuid = localStorage.getItem('myUuid');
if (!myUuid) {
myUuid = guid();
localStorage.setItem('myUuid', myUuid);
}
//
// Mapbox
//
var map;
var markers = {};
var mapZooming = false;
L.mapbox.accessToken = config.mapbox.accessToken;
map = L.mapbox.map('map', config.mapbox.mapId, {
zoomControl: false,
attributionControl: false,
tileLayer: {
maxNativeZoom: 19
}
}).setView([48.861920, 2.341755], 18)
// map.on('ready', function() { console.log('map.ready') });
// https://github.com/bbecquet/Leaflet.PolylineDecorator
L.RotatedMarker = L.Marker.extend({
options: { angle: 0 },
_setPos: function(pos) {
L.Marker.prototype._setPos.call(this, pos);
if (L.DomUtil.TRANSFORM) {
// use the CSS transform rule if available
this._icon.style[L.DomUtil.TRANSFORM] += ' rotate(' + this.options.angle + 'deg)';
} else if (L.Browser.ie) {
// fallback for IE6, IE7, IE8
var rad = this.options.angle * L.LatLng.DEG_TO_RAD,
costheta = Math.cos(rad),
sintheta = Math.sin(rad);
this._icon.style.filter += ' progid:DXImageTransform.Microsoft.Matrix(sizingMethod=\'auto expand\', M11=' +
costheta + ', M12=' + (-sintheta) + ', M21=' + sintheta + ', M22=' + costheta + ')';
}
}
});
L.rotatedMarker = function(pos, options) {
return new L.RotatedMarker(pos, options);
};
map.on('zoomstart', function() {
mapZooming = true
})
map.on('zoomend', function() {
mapZooming = false
})
function createIcon(uuid, point) {
var color
var svg;
if (uuid === myUuid) {
// Own marker
color = '#2196f3'
} else if (point.timestamp < now() - 60) {
// Inactive marker
color = '#bdbdbd'
} else {
// Others marker
color = '#ff9800'
}
var svg = '<svg width="70" height="70" xmlns="http://www.w3.org/2000/svg">'
+ '<path fill="#fff" d="m35,18.000002c-9.400002,0 -17,7.599995 -17,16.999998s7.599998,17 17,17s17,-7.599998 17,-17s-7.599998,-16.999998 -17,-16.999998zm0,30.999998c-7.700001,0 -14,-6.299999 -14,-14s6.299999,-13.999998 14,-13.999998s14,6.299997 14,13.999998s-6.300003,14 -14,14z"/>'
+ '<circle fill="' + color + '" stroke="null" r="14.031405" cy="35.000002" cx="34.999999"/>'
+ (point.orientation ? '<polygon fill="' + color + '" points="47.699997901916504,16.983383178710938 47.000000953674316,17.68338394165039 35.000000953674316,12.7833890914917 23.00000286102295,17.68338394165039 22.300002098083496,16.983383178710938 35.000000953674316,4.28338623046875" />' : '')
+ '</svg>'
return L.icon({
iconUrl: 'data:image/svg+xml;base64,' + btoa(svg),
iconSize: [40, 40],
})
}
I found this code on Github and im trying to set a name to the marker but I am having trouble doing so can some one point me in the right direction I would like to display a name when you hover over the marker.
function addPoint(uuid, point) {
var marker = L.rotatedMarker([point.coords.latitude, point.coords.longitude], {
//zIndexOffset: (uuid === myUuid ? 1000 : 0),
icon: createIcon(uuid, point)
})
markers[uuid] = marker;
marker.options.angle = point.orientation;
marker.addTo(map)
map.fitBounds(Object.keys(markers).map(function(uuid) {
return markers[uuid].getLatLng()
}))
}
function removePoint(uuid) {
if (markers[uuid]) {
map.removeLayer(markers[uuid])
//markers[uuid] = null
}
}
function updatePoint(uuid, point) {
// Avoid clipping effect when zooming map + updating point at the same time.
if (mapZooming) {
map.once('zoomend', function() {
updatePoint(uuid, point)
})
return
}
var marker = markers[uuid]
marker.setIcon(createIcon(uuid, point));
marker.options.angle = point.orientation
marker.setLatLng([point.coords.latitude, point.coords.longitude])
}
function putPoint(uuid, point) {
if (markers[uuid]) {
updatePoint(uuid, point)
} else {
addPoint(uuid, point)
}
}
In Fire Base instead of some random number and letters I would like to give the markers names
//
// Firebase
//
var endpoint;
endpoint = new Firebase('https://' + config.firebase + '.firebaseio.com/maps/' + mapId);
endpoint.on('child_added', function(childSnapshot) {
var uuid = childSnapshot.key()
var point = childSnapshot.val()
if (uuid === myUuid) return
addPoint(uuid, point)
})
endpoint.on('child_changed', function(childSnapshot) {
var uuid = childSnapshot.key()
var point = childSnapshot.val()
if (uuid === myUuid) return
putPoint(uuid, point)
})
endpoint.on('child_removed', function(oldChildSnapshot) {
var uuid = oldChildSnapshot.key()
removePoint(uuid)
})
//
// Tracking
//
var watchPositionId;
var currentCoords = null;
var currentOrientation = null;
function pushCurrentStatus() {
if (!currentCoords) return
endpoint.child(myUuid).set({
coords: {
latitude: currentCoords.latitude,
longitude: currentCoords.longitude,
},
orientation: currentOrientation,
timestamp: now()
})
}
pushCurrentStatus = _.throttle(pushCurrentStatus, 50)
if (navigator.geolocation) {
setTimeout(function() {
watchPositionId = navigator.geolocation.watchPosition(
successWatchPosition,
failWatchPosition,
{enableHighAccuracy: false}
)
}, 0)
setTimeout(function() {
navigator.geolocation.clearWatch(watchPositionId)
watchPositionId = navigator.geolocation.watchPosition(
successWatchPosition,
failWatchPosition,
{enableHighAccuracy: true}
)
}, 5000)
}
function successWatchPosition(position) {
if (!position.coords) return
currentCoords = position.coords
pushCurrentStatus()
putPoint(myUuid, {coords: currentCoords, orientation: currentOrientation})
}
function failWatchPosition() {
alert('Fail to get your location')
}
if (window.DeviceOrientationEvent) {
window.addEventListener('deviceorientation', deviceOrientationHandler, true)
}
function deviceOrientationHandler(event) {
var alpha;
if (event.webkitCompassHeading) {
alpha = event.webkitCompassHeading;
} else {
alpha = event.alpha;
}
if (!alpha) return
currentOrientation = 360 - alpha
pushCurrentStatus()
putPoint(myUuid, {coords: currentCoords, orientation: currentOrientation})
}
//
// Remove old markers
//
setInterval(function() {
endpoint.limitToFirst(100).once('value', function(snap) {
var now = Math.floor(Date.now() / 1000)
snap.forEach(function(childSnapshot) {
var uuid = childSnapshot.key()
var point = childSnapshot.val()
if (uuid === myUuid) return
if (childSnapshot.val().timestamp < now - 60 * 30) {
endpoint.child(uuid).set(null)
} else {
updatePoint(uuid, point)
}
})
})
}, 5000);
})();
We use an embedded map to track our location while driving in the field. Currently the map rotates to match the GPS's orientation. We've found that to be very disorienting and I'd like to lock the orientation North (0 degrees). I still would like the map to track location and indicate heading if available. Below is the snipped from the map's javascript file pertaining to geolocation.
map.addLayer(addressLayer);
// Geolocation marker
var markerEl = document.getElementById('geolocation_marker');
var marker = new ol.Overlay({
positioning: 'center-center',
element: markerEl,
stopEvent: false
});
map.addOverlay(marker);
// LineString to store the different geolocation positions. This LineString
// is time aware.
// The Z dimension is actually used to store the rotation (heading).
var positions = new ol.geom.LineString([],
/** #type {ol.geom.GeometryLayout} */ ('XYZM'));
// Geolocation Control
var geolocation = new ol.Geolocation(/** #type {olx.GeolocationOptions} */ ({
projection: view.getProjection(),
tracking: true,
trackingOptions: {
maximumAge: 10000,
enableHighAccuracy: true,
timeout: 600000
}
}));
var deltaMean = 500; // the geolocation sampling period mean in ms
// Listen to position changes
geolocation.on('change', function(evt) {
var position = geolocation.getPosition();
var accuracy = geolocation.getAccuracy();
var heading = geolocation.getHeading() || 0;
var speed = geolocation.getSpeed() || 0;
var m = Date.now();
addPosition(position, heading, m, speed);
map.getView().setCenter(geolocation.getPosition());
document.getElementById("locate").style.backgroundColor = 'rgba(0,128,0,1)';
locateUser = true;
});
geolocation.on('error', function(error) {
var errors = {
1: 'Permission denied to locate device',
2: 'Position unavailable',
3: 'Request timeout'
};
if (error.code){
document.getElementById("locate").style.backgroundColor = 'rgba(255,0,0,1)';
locateUser = false;
}
alert("Error: " + errors[error.code]);
});
// convert radians to degrees
function radToDeg(rad) {
return rad * 360 / (Math.PI * 2);
}
// convert degrees to radians
function degToRad(deg) {
return deg * Math.PI * 2 / 360;
}
// modulo for negative values
function mod(n) {
return ((n % (2 * Math.PI)) + (2 * Math.PI)) % (2 * Math.PI);
}
function addPosition(position, heading, m, speed) {
var x = position[0];
var y = position[1];
var fCoords = positions.getCoordinates();
var previous = fCoords[fCoords.length - 1];
var prevHeading = previous && previous[2];
if (prevHeading) {
var headingDiff = heading - mod(prevHeading);
// force the rotation change to be less than 180°
if (Math.abs(headingDiff) > Math.PI) {
var sign = (headingDiff >= 0) ? 1 : -1;
headingDiff = - sign * (2 * Math.PI - Math.abs(headingDiff));
}
heading = prevHeading + headingDiff;
}
positions.appendCoordinate([x, y, heading, m]);
// only keep the 20 last coordinates
positions.setCoordinates(positions.getCoordinates().slice(-20));
// FIXME use speed instead
if (heading && speed) {
markerEl.src = 'images/geolocation_marker_heading.png';
} else {
markerEl.src = 'images/geolocation_marker.png';
}
}
var previousM = 0;
// change center and rotation before render
map.beforeRender(function(map, frameState) {
if (frameState !== null) {
// use sampling period to get a smooth transition
var m = frameState.time - deltaMean * 1.5;
m = Math.max(m, previousM);
previousM = m;
// interpolate position along positions LineString
var c = positions.getCoordinateAtM(m, true);
var view = frameState.viewState;
if (c) {
view.rotation = -c[2];
marker.setPosition(c);
}
}
return true; // Force animation to continue
});
// postcompose callback
function render() {
map.render();
}
// geolocate device
var geolocateBtn = document.getElementById('locate');
geolocateBtn.addEventListener('click', function() {
if(locateUser){
geolocation.setTracking(false);
geolocateBtn.style.backgroundColor = 'rgba(255,0,0,1)';
locateUser = false;
}
else{
geolocation.setTracking(true);
map.getView().setCenter(geolocation.getPosition());
geolocateBtn.style.backgroundColor = 'rgba(0,128,0,1)';
map.on('postcompose', render);
map.render();
locateUser = true;
}
}, false);
addLocations(QueryString);
function addLocations(addressArr) {
if (nextAddress < addressArr.length) {
setTimeout(function(){
if (addressArr[nextAddress] !== undefined){
geocodeAddress(addressArr[nextAddress]);
}
}, delay);
}
if(nextAddress == addressArr.length) {
view.fitExtent(vectorSource.getExtent(), map.getSize());
}
}
function geocodeAddress (location) {
$.getJSON('http://maps.googleapis.com/maps/api/geocode/json?address='+location.address+'&sensor=false', null, function (data) {
if(data.status === 'OK'){
var p = data.results[0].geometry.location;
var color = location.status == 'incomplete' ? 'red' : 'green';
var pointFeature = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.transform([p.lng, p.lat], 'EPSG:4326',
'EPSG:3857')),
fillColor: color,
id: location.id
});
vectorSource.addFeature(pointFeature);
addresses.push(pointFeature);
nextAddress+=1;
addLocations(QueryString);
}
if(data.status === 'OVER_QUERY_LIMIT'){
delay += delay;
}
});
}
Here is the relevant ol3 code that's causing the rotation to happen.
By setting view.rotation = 0; you may resolve the issue.
I am unable to do pinch and zoom on ipad, I guess that the below given code may be effecting the code,
Please give any perfect solution.
Is there any issue with the binding of body child events or need to calculate the touches in different variables, and do manual calculation.
///I-Pad evenet Binding
$(document).ready(function () {
$("body").children().bind('touchstart touchmove touchend touchcancel', function () {
var touches = event.changedTouches, first = touches[0], type = "";
switch (event.type) {
case "touchstart": type = "mousedown";
break;
case "touchmove": type = "mousemove";
break;
case "touchend": type = "mouseup";
break;
default: return;
}
var simulatedEvent = document.createEvent("MouseEvent");
simulatedEvent.initMouseEvent(type, true, true, window, 1,
first.screenX, first.screenY,
first.clientX, first.clientY, false,
false, false, false, 0/*left*/, null);
if (touches.length < 2) {
first.target.dispatchEvent(simulatedEvent);
event.preventDefault();
}
});
(function () {
var last_x = null, last_y = null, w_area = workarea[0],
panning = false, keypan = false, dopanning = false;
$("#svgroot").bind('mousemove mouseup', function (evt) {
if (dopanning === false) return;
var clientxnew = +$("#svgcontent")[0].getAttribute("x"),
clientynew = +$("#svgcontent")[0].getAttribute("y");
clientxnew += (evt.clientX - last_x);
clientynew += (evt.clientY - last_y);
last_x = evt.clientX;
last_y = evt.clientY;
//this.setAttribute("viewBox", vb.join(' '));
// updateCanvas(true);
$("#svgcontent").show();
$("#svgcontent")[0].setAttribute("x", clientxnew);
$("#svgcontent")[0].setAttribute("y", clientynew);
svgedit.select.getSelectorManager().selectorParentGroup.setAttribute("transform", "translate(" + clientxnew + "," + clientynew + ")");
if (evt.type === 'mouseup') { dopanning = false; }
return false;
}).mousedown(function (evt) {
var mouse_target = svgCanvas.getMouseTarget(evt);
if (svgCanvas.getMode() == "text" || svgCanvas.getMode() == "textedit") {
dopanning = false; return;
}
if ((mouse_target.id.indexOf("grouplayerdragged") > -1 || mouse_target.id.indexOf("hotspot") > -1 ||
mouse_target.id.indexOf("clonediv") > -1 || mouse_target.tagName === "text")) { dopanning = false; return; }
if (selectedElement != null) {
dopanning = false; return;
}
if (evt.button === 0) {
dopanning = true;
last_x = evt.clientX;
last_y = evt.clientY;
svgCanvas.clearSelection(true);
return false;
}
});
$(window).mouseup(function () {
panning = false;
dopanning = false;
});
you should use gesturechange. try this way (this is not your exact solution but you can hack it)
var angle = 0;
var newAngle;
var scale = 1;
var newScale;
function saveChanges() {
angle = newAngle;
scale = newScale;
}
function getAngleAndScale(e) {
// Don't zoom or rotate the whole screen
e.preventDefault();
// Rotation and scale are event properties
newAngle = angle + e.rotation;
newScale = scale * e.scale;
// Combine scale and rotation into a single transform
var tString = "scale(" + newScale + ")";
document.getElementById("theDiv").style.webkitTransform = tString;
}
function init() {
// Set scale and rotation during gestures
document.getElementById("theDiv").addEventListener("gesturechange", getAngleAndScale, false);
// Preserve scale and rotation when gesture ends
document.getElementById("theDiv").addEventListener("gestureend", saveChanges, false);
}
<body>
<div style="width:300px;height:300px;overflow:scrool;-webkit-overflow-scrolling: touch;">
<img id="theDiv" style="width:100%;" src="http://animal.discovery.com/guides/wild-birds/gallery/mallard_duck.jpg" />
</div>
Try this on your ipad
I am using the following code to tween an movieclip once _global.choiceMade equals 1...
onClipEvent (load) {
import mx.transitions.Tween;
import mx.transitions.easing.*;
}
onClipEvent (enterFrame) {
if (_global.choiceMade == 1) {
var myTweenX:Tween = new Tween(this, "_x", mx.transitions.easing.Back.easeOut, this._x, -349, 0.5, True);
}
}
This is contained within each frame of the main timeline. The first time it runs fine but on the next frame it runs incredibly slow (takes about 12 seconds not 0.5 and is very choppy), if I then return to the first frame and run it again now this time it is extremely slow.
I can't work out why its doing this my CPU stays around 6-15% while its running so it can't be too demanding.
Updated to show rest of my code:
On main timeline have a frame each containing a movieclip. On the timeline of each of these movieclips contains:
//tint an object with a color just like Effect panel
//r, g, b between 0 and 255; amount between 0 and 100
Color.prototype.setTint = function(r, g, b, amount) {
var percent = 100-amount;
var trans = new Object();
trans.ra = trans.ga=trans.ba=percent;
var ratio = amount/100;
trans.rb = r*ratio;
trans.gb = g*ratio;
trans.bb = b*ratio;
this.setTransform(trans);
};//Robert Penner June 2001 - http://www.robertpenner.com
MovieClip.prototype.scaleXY = function(to){
this.onEnterFrame = function(){
this._alpha = to-(to-this._alpha)/1.2;
if(this._alpha > to-1 && this._alpha < to+1){
this._alpha = to;
delete this.onEnterFrame
}
}
}
scoreUpdated = 0;
Answer = 1;
_global.choiceMade = 0;
Buttons = new Array(this.buttonHolder.True, this.buttonHolder.False);
Answers = new Array(this.Correct, this.Wrong);
for (i=0; i<Answers.length; i++) {
Answers[i]._alpha = 0;
}
for (b=0; b<Buttons.length; b++) {
Buttons[b].thisValue = b;
}
In this movieclip there are two movieclip buttons (True and False) containing this code:
onClipEvent (enterFrame) {
this.onRollOver = function() {
this.gotoAndStop("over");
};
this.onRollOut = function() {
this.gotoAndStop("up");
};
this.onPress = function() {
this.gotoAndStop("down");
};
this.onReleaseOutside = function() {
this.gotoAndStop("up");
};
this.onRelease = function() {
this.gotoAndStop("down");
whichChoice = this;
_global.choiceMade = 1;
counter = 0;
};
if (_global.choiceMade == 1) {
this.enabled = false;
this._parent.scoreNow = _global.score;
this._parent.scoreOutOf = (this._parent._parent._currentframe)- 1 + ( _global.choiceMade);
if (thisValue == this._parent._parent.Answer && whichChoice == this) {
myColor = new Color(this);
myColor.setTint(0,204,0,13);
this._parent._parent.Answers[0]._alpha = 100;
this._parent._parent.Answers[0].scaleXY(100);
this.tick.swapDepths(1000);
if (counter == 0) {
_global.score++;
counter++;
}
}
else if (thisValue == this._parent._parent.Answer) {
myColor = new Color(this);
myColor.setTint(0,204,0,13);
this.tick.swapDepths(1000);
}
else if (whichChoice == this) {
this._parent._parent.Answers[1]._alpha = 100;
this._parent._parent.Answers[1].scaleXY(100);
myColor = new Color(this);
myColor.setTint(255,0,0,13);
this.cross.swapDepths(1000);
}
else {
myColor = new Color(this);
myColor.setTint(255,0,0,13);
myColor.setTint(255,0,0,13);
this.cross.swapDepths(1000);
}
}
}
The script at the top is on a movieclip these buttons are contained in called buttonHolder which does what it says, and tweens the buttons across the screen to reveal a next button once an answer is chosen.
From what I can see, as long as you have choiceMade == 1 you create a new effect! which is not ok. because in 1 sec at 15 fps you will have 15 tweens running :(
try yo set choiceMade = 0 or somehting else than 1
onClipEvent (enterFrame)
{
if (_global.choiceMade == 1)
{
_global.choiceMade = -1;
var myTweenX:Tween = new Tween(this, "_x", mx.transitions.easing.Back.easeOut, this._x, -349, 0.5, True);
}
}
Without seeing the rest of your code it's hard to see exactly what's going on. But it looks like you never change choiceMade and it continuously recreates the tween.
onClipEvent (enterFrame) {
if (_global.choiceMade == 1) {
var myTweenX:Tween = new Tween(this, "_x", mx.transitions.easing.Back.easeOut, this._x, -349, 0.5, True);
_global.choiceMade = 0;
}
}