Related
I have some highcharts which are synchronized and use the following code to synchronise the crosshairs as the mouse is moved:
//catch mousemove event and have all charts' crosshairs move along indicated values on x axis
function syncronizeCrossHairs(chart) {
['mousemove', 'touchmove', 'touchstart'].forEach(function(eventType) {
var container = $(chart.container),
offset = container.offset(),
x;
container[0].addEventListener(eventType,
(function(evt) {
x = evt.clientX - chart.plotLeft - offset.left;
Highcharts.charts.forEach(ch => {
var e = ch.pointer.normalize(evt), // Find coordinates within the chart
points = [];
ch.series.forEach(s => {
var point = s.searchPoint(e, true);
if (point) {
point.setState();
if (s.visible) {
points.push(point)
}
}
})
if (points) {
var number = 0;
Highcharts.each(points, function(p, i) {
if (!p.series.visible) {
points.splice(i - number, 1);
number++;
}
})
if (points.length) {
ch.tooltip.refresh(points); // Show the tooltip
}
}
ch.xAxis[0].drawCrosshair(x, points[0])
})
}))
})
}
I am now getting the following console message:
Highcharts error #32: www.highcharts.com/errors/32/?Highcharts.each=Array.forEach
Highcharts.each: Array.forEach
Can someone advise how I can replace the Highcharts.each command in my code?
Thanks
I think that changing:
Highcharts.each(points, function(p, i) {
if (!p.series.visible) {
points.splice(i - number, 1);
number++;
}
})
to:
points.forEach((p, i) => {
if (!p.series.visible) {
points.splice(i - number, 1);
number++;
}
})
Should be enough. Please test it in your application and let me know if it helped.
Wherever you have a Highcharts.each just change it to a for loop. Here's a before and after example from my project with synced charts:
Before (Using the deprecated Highcharts.each way):
Highcharts.each(Highcharts.charts, function (chart) {
if (chart && chart !== thisChart) {
if (chart.xAxis[0].setExtremes) {
chart.xAxis[0].setExtremes(e.min, e.max, undefined, false, {
trigger: "syncExtremes"
});
}
}
});
And after, just simply changing it to a for loop:
for (let i = 0; i < Highcharts.charts.length; i++) {
let chart = Highcharts.charts[i];
if (chart && chart !== thisChart) {
if (chart.xAxis[0].setExtremes) {
chart.xAxis[0].setExtremes(e.min, e.max, undefined, false, {
trigger: "syncExtremes"
});
}
}
}
I have created a series of synchronised charts, the code to link the crosshair and tooltip is rather complicated:
function syncronizeCrossHairs(chart) {
['mousemove', 'touchmove', 'touchstart'].forEach(function(eventType) {
var container = $(chart.container),
offset = container.offset(),
x;
container[0].addEventListener(eventType,
(function(evt) {
x = evt.clientX - chart.plotLeft - offset.left;
//remove old plot line and draw new plot line (crosshair) for this chart
var xAxis1 = chart1.xAxis[0],
points = [],
points1 = [],
points2 = [],
points3 = [],
e = chart1.pointer.normalize(evt); // Find coordinates within the chart
chart1.series.forEach(s => {
var point = s.searchPoint(e, true)
if (point) {
point.setState();
points.push(point)
}
})
if (points) {
var number = 0;
Highcharts.each(points, function(p, i) {
if (!p.series.visible) {
points.splice(i - number, 1);
number++;
}
})
if (points.length) {
chart1.tooltip.refresh(points); // Show the tooltip
}
}
xAxis1.drawCrosshair(x, points[0])
/*----- second chart ------*/
var xAxis2 = chart2.xAxis[0];
chart2.series.forEach(s => {
var point = s.searchPoint(e, true)
if (point) {
point.setState();
points1.push(point)
}
})
if (points1[0]) {
var number = 0;
Highcharts.each(points1, function(p, i) {
if (!p.series.visible) {
points1.splice(i - number, 1);
number++;
}
})
if (points1.length) {
chart2.tooltip.refresh(points1); // Show the tooltip
}
}
xAxis2.drawCrosshair(x, points1[0])
/*----- third chart ------*/
var xAxis3 = chart3.xAxis[0];
chart3.series.forEach(s => {
var point = s.searchPoint(e, true)
if (point) {
point.setState();
points2.push(point)
}
console.log(points2)
})
if (points2[0]) {
var number = 0;
Highcharts.each(points1, function(p, i) {
if (!p.series.visible) {
points2.splice(i - number, 1);
number++;
}
})
if (points2.length) {
chart3.tooltip.refresh(points2); // Show the tooltip
}
}
xAxis3.drawCrosshair(x, points2[0])
/* ----- fourth chart ------ */
var xAxis4 = chart4.xAxis[0];
chart4.series.forEach(s => {
var point = s.searchPoint(e, true)
if (point) {
point.setState();
points3.push(point)
}
})
if (points3[0]) {
var number = 0;
Highcharts.each(points3, function(p, i) {
if (!p.series.visible) {
points3.splice(i - number, 1);
number++;
}
})
if (points3.length) {
chart4.tooltip.refresh(points3); // Show the tooltip
}
}
xAxis4.drawCrosshair(x, points3[0])
}))
})
}
How ever I have found a better example here: http://jsfiddle.net/mushigh/a3kjrz6u/
$('#container').bind('mousemove touchmove touchstart', function(e) {
var chart,
point,
i,
event;
for (i = 0; i < Highcharts.charts.length; i = i + 1) {
chart = Highcharts.charts[i];
event = chart.pointer.normalize(e.originalEvent); // Find coordinates within the chart
point = chart.series[0].searchPoint(event, true); // Get the hovered point
if (point) {
point.onMouseOver(); // Show the hover marker
chart.tooltip.refresh(point); // Show the tooltip
chart.xAxis[0].drawCrosshair(event, point); // Show the crosshair
}
}
});
/**
* Override the reset function, we don't need to hide the tooltips and crosshairs.
*/
Highcharts.Pointer.prototype.reset = function() {
return undefined;
};
How do I adapt my code here: https://jsfiddle.net/ashenshugar/716jx4n9/
To use the simplified code in my example
I don't think that the demo which you have found is a good approach to your requirements.
The demo needs to the specific data structure, like: https://github.com/highcharts/highcharts/blob/master/samples/data/activity.json
The demo is an example to show tooltip for only one series per chart, meanwhile, you have got a few and a shared tooltip, so finally, you will need to do the same calculations to get the array of points for shared tooltip.
Except that, I think that a better approach is to clean up your code.
Notice that the functionalities to calculate points for each chart are similar and can be paste into the loop:
function syncronizeCrossHairs(chart) {
['mousemove', 'touchmove', 'touchstart'].forEach(function(eventType) {
var container = $(chart.container),
offset = container.offset(),
x;
container[0].addEventListener(eventType,
(function(evt) {
x = evt.clientX - chart.plotLeft - offset.left;
Highcharts.charts.forEach(ch => {
var e = ch.pointer.normalize(evt), // Find coordinates within the chart
points = [];
ch.series.forEach(s => {
var point = s.searchPoint(e, true);
if (point) {
point.setState();
points.push(point)
}
})
if (points) {
var number = 0;
Highcharts.each(points, function(p, i) {
if (!p.series.visible) {
points.splice(i - number, 1);
number++;
}
})
if (points.length) {
ch.tooltip.refresh(points); // Show the tooltip
}
}
ch.xAxis[0].drawCrosshair(x, points[0])
})
}))
})
}
And notice that also your afterSetextreme callback can be change to trigger this function:
function setExtremes(chart, min, max) {
Highcharts.charts.forEach(ch => {
if (ch !== chart) {
ch.xAxis[0].setExtremes(min, max)
}
})
}
Also, you can define options which are common for each chart, like it is done here: https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/demo/gauge-solid/
Finally demo: https://jsfiddle.net/BlackLabel/hv7azdgm/
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);
});
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