Chartjs 2.9.4 - clickable label - javascript

I'm trying to make linkable label to redirect to a page when clicked (i use this snippet: Add Link to X-Label Chart.js). I'm using the 2.9.4 version of ChartJS.
This is my code:
var ctx = document.getElementById("barChart-mostSold").getContext('2d');
this.chartmostSold =new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: {{listTopIssuerSoldLabel | tojson }},
datasets: [
{
data: {{listTopIssuerSoldValue | tojson }}
}
]
},
options: {
events: ["mousemove", "mouseout", "click", "touchstart", "touchmove", "touchend"],
onClick: function(e, data) {
var ctx = $("#barChart-mostSold")[0].getContext("2d");
var base = this.chart.chartArea.bottom;
var height = this.chart.chart.height;
var width = this.chart.chart.scales['x-axis-0'].width;
var offset = $('#chartmostSold').offset().top - $(window).scrollTop();
if(e.pageY > base + offset){
var count = this.chart.scales['x-axis-0'].ticks.length;
var padding_left = this.chart.scales['x-axis-0'].paddingLeft;
var padding_right = this.chart.scales['x-axis-0'].paddingRight;
var xwidth = (width-padding_left-padding_right)/count;
// don't call for padding areas
var bar_index = (e.offsetX - padding_left - this.chart.scales['y-axis-0'].width) / xwidth;
if(bar_index > 0 & bar_index < count){
bar_index = Math.floor(bar_index);
console.log(bar_index);
}
}
}
}
});
I got this error:
Uncaught TypeError: Cannot read property 'top' of undefined
at tn.onClick ((index):1141)
at tn.handleEvent (VM64 Chart.min.js:7)
at tn.eventHandler (VM64 Chart.min.js:7)
at n (VM64 Chart.min.js:7)
at HTMLCanvasElement.Se.<computed> (VM64 Chart.min.js:7)

Related

Chart.js not showing all labels on pie chart

I recently updated Chart.js and got the following bug afterwards.
Some of the labels under my pie chart are not showing anymore, until I click on one of the visible ones. Then everything shows as it should.
Here you can see how it looks before clicking on something:
before clicking
and here you can see the chart after clicking on a visible label: after clicking
Here you can see the options section of my chart code:
options: {
maintainAspectRatio : false,
animation: {
duration: 0
},
elements: {
center: center
},
legend: {
position: 'bottom',
//fullWidth: 'true',
labels: {
generateLabels: (chart) => {
chart.legend.afterFit = function () {
var width = this.width;
this.lineWidths = this.lineWidths.map( () => this.width - 12 );
this.options.labels.padding = 30;
this.options.labels.boxWidth = 15;
};
var data = chart.data;
if (data.labels.length && data.datasets.length) {
return data.labels.map((label, i) => {
var meta = chart.getDatasetMeta(0);
var ds = data.datasets[0];
var arc = meta.data[i];
var custom = arc && arc.custom || {};
var getValueAtIndexOrDefault = this.getValueAtIndexOrDefault;
var arcOpts = chart.options.elements.arc;
var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
return {
text: label,
fillStyle: fill,
strokeStyle: stroke,
lineWidth: bw,
hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
// Extra data used for toggling the correct item
index: i
};
});
}
return [];
}
},
},
cutoutPercentage: 85,
plugins: {
datalabels: {
anchor: 'end',
align: 'end',
display: function(context) {
var index = context.dataIndex;
var value = context.dataset.data[index];
return value > 0; // display labels with a value greater than 0
},
font: {
weight: 'bold',
size: 16,
},
}
}
}
chart.legend.afterFit = function () {
if (keys.length > 4) {
this.options.labels.padding = 15;
this.height = this.height + 25;
}
else {
this.options.labels.padding = 20;
this.height = this.height + 18;
}
var width = this.width; // guess you can play with this value to achieve needed layout
this.lineWidths = this.lineWidths.map(function () { return width; });
};
I changed the part at afterFit to this, so it is formatting right, the problem is, the padding settings are set back when i click on a label.

Line chart slice to calculate difference (actual and percentage) between two points

I have an annotated line chart built successfully from a CSV file using google annotation charts. (Thanks Whitehat for your help).
I have looked unsuccessfully through the google chart examples to find a way of grabbing a slice of the line chart so as to then perform some calculations between the two points e.g. difference and percentage difference. There may be further calculations I wish to do but these two are enough for the moment.
Essentially I am trying to build a feature like Google's stock chart
Code so far:
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.71/jquery.csv-0.71.min.js"></script>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type='text/javascript'>
// load google charts
google.charts.load('current', {
packages: ['annotationchart']
}).then(function () {
// declare data variable
var arrayData;
// get csv data
$.get('test.csv', function(csvString) {
// get csv data success, convert to an array, draw chart
arrayData = $.csv.toArrays(csvString, {onParseValue: $.csv.hooks.castToScalar});
drawChart(arrayData);
})
});
// draw chart
function drawChart(arrayData) {
// convert string in first column to a date
arrayData = arrayData.map(function (row) {
return [new Date(row[0]),row[1],row[2]];
});
// create google data table, chart, and options
var data = google.visualization.arrayToDataTable(arrayData);
var chart = new google.visualization.AnnotationChart(document.getElementById('chart_div'));
var options = {
displayAnnotations: true
};
// draw chart
chart.draw(data, options);
}
</script>
</head>
<body>
<div id='chart_div' style='width: 1200x; height: 700px;'></div>
</body>
</html>
Any ideas how I can go about doing this?
you could use mouse events to allow the user to draw a selection on the chart.
given the coordinates of the selection,
use chart methods getChartLayoutInterface & getHAxisValue,
to determine the range of values the user selected.
see following working snippet,
click the chart and hold the mouse, then drag to draw the selection.
when the mouse is let go, the values selected will be displayed.
google.charts.load('current', {
packages: ['controls', 'corechart']
}).then(function () {
// build data table
var oneDay = (24 * 60 * 60 * 1000);
var dateEnd = new Date();
var dateStart = new Date(dateEnd.getTime() - (oneDay * 365.25));
var data = new google.visualization.DataTable();
data.addColumn('date', 'Date');
data.addColumn('number', 'Y');
for (var i = dateStart.getTime(); i <= dateEnd.getTime(); i = i + oneDay) {
var direction = (i % 2 === 0) ? 1 : -1;
var rowDate = new Date(i);
data.addRow([rowDate, rowDate.getFullYear() + (rowDate.getDate() * direction)]);
}
// chart options
var options = {
chartArea: {
height: '100%',
width: '100%',
top: 24,
left: 60,
right: 16,
bottom: 60
},
hAxis: {
format: 'MMM-yyyy'
},
height: '100%',
legend: {
position: 'top'
},
width: '100%'
};
// create chart and elements
var container = document.getElementById('chart');
var values = document.getElementById('values');
var chart = new google.visualization.LineChart(container);
// wait until chart is ready
google.visualization.events.addListener(chart, 'ready', function () {
// initialize variables
var chartLayout = chart.getChartLayoutInterface();
var chartArea = chartLayout.getChartAreaBoundingBox();
var chartBounds = container.getBoundingClientRect();
var select = document.getElementById('select');
var x1 = 0;
var y1 = 0;
var x2 = 0;
var y2 = 0;
var x3 = 0;
var y3 = 0;
var x4 = 0;
var y4 = 0;
// listen for mouse events
window.addEventListener('mousedown', function (e) {
select.className = '';
x1 = e.pageX;
y1 = e.pageY;
reCalc();
});
window.addEventListener('mousemove', function (e) {
if (select.className === '') {
x2 = e.pageX;
y2 = e.pageY;
reCalc();
}
});
window.addEventListener('mouseup', function (e) {
select.className = 'static';
selectPoints();
});
// show user selection
function reCalc() {
x3 = Math.min(x1,x2);
x4 = Math.max(x1,x2);
y3 = Math.min(y1,y2);
y4 = Math.max(y1,y2);
select.style.left = x3 + 'px';
select.style.width = x4 - x3 + 'px';
select.style.top = (chartBounds.top + chartArea.top + window.pageYOffset) + 'px';
select.style.height = (chartArea.height + window.pageYOffset) + 'px';
}
// show values from selection
function selectPoints() {
if ((((chartBounds.left + window.pageXOffset) <= x3) &&
((chartBounds.left + chartBounds.width + window.pageXOffset) >= x4)) &&
(((chartBounds.top + window.pageYOffset) <= y3) &&
((chartBounds.top + chartBounds.height + window.pageYOffset) >= y4))) {
var rows = data.getFilteredRows([{
column: 0,
minValue: chartLayout.getHAxisValue(x3),
maxValue: chartLayout.getHAxisValue(x4)
}]);
values.innerHTML = '';
rows.forEach(function (index) {
var value = values.appendChild(document.createElement('div'));
value.innerHTML = data.getFormattedValue(index, 0) + ': ' + data.getFormattedValue(index, 1);
});
}
}
});
// draw chart
chart.draw(data, options);
});
#select {
background-color: #3366cc;
border: 1px solid #3366cc;
opacity: 0.2;
position: absolute;
z-index: 1000;
}
.hidden {
display: none;
visibility: hidden;
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div class="hidden" id="select"></div>
<div id="chart"></div>
<div id="values"></div>

Disable dataLabel if its width is more than height of its bar in highCharts

I have created a chart using HighCharts and I want to disable any dataLabel if its width is more than height of its bar(bar cannot accomodate the dataLabel). I set the property 'enabled : false' of dataLabel of particular points but the effect is not being reflected in the chart.
<html>
<body>
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.1.1.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<div id="container" style="min-width: 100px; max-width: 400px; height: 400px; margin: 0 auto"></div>
<script language="javascript">
var pointSelected = {};
var pointHovered = {};
//Highcharts.chart('container',
var chartObject = {
chart: {
type: 'bar',
events : {
render : function(){
var ch = this;
var series = this.series;
ch.series.forEach(function(s){
s.points.forEach(function(point){
var barHeight = point.graphic.element.height.animVal.value;
var dataLabelWidth = point.dataLabel.width;
var plotBoxWidth = ch.plotBox.width;
console.log(plotBoxWidth);
if(barHeight + dataLabelWidth < plotBoxWidth) {
// console.log(barHeight + dataLabelWidth);
// console.log("point will lie inside");
}
//else{
if(dataLabelWidth > barHeight){
//USING JQUERY IT CAN BE DONE BUT I WANT TO AVOID JQUERY AS MUCH AS POSSIBLE $(point.dataLabel.element).fadeOut("fast");
point.dataLabel.alignOptions.enabled = false; //THIS IS WHERE I'M DISABLING POINT
console.log(point)
// point.update({dataLabels : {enabled : false}});
//ch.options.plotOptions.series.dataLabels.enabled = false;
}
if(barHeight + dataLabelWidth > plotBoxWidth){
// console.log(barHeight + dataLabelWidth);
var diff = barHeight + dataLabelWidth - plotBoxWidth;
// console.log(diff);
// var x = point.dataLabel.translateX;
// var y = point.dataLabel.translateY;
// console.log(x);
// console.log(point);
//// diff +=15;
var diff2 = barHeight - dataLabelWidth;
// console.log("diff2" + diff2);
point.dataLabel.translate( diff2 , point.dataLabel.alignAttr.y );
// console.log( point.dataLabel.text);
//point.dataLabel.stork("black");
// point.dataLabel.text.styles.fill = "black";
var elem = $(point.dataLabel.element).children();
$(elem).eq(0).css("fill" , "black");
// console.log(elem);
// $(textElem).attr("style" , "fill : black");
// console.log(textElem);
// $(point.dataLabel.element.innerHTML).eq(0).children().eq(0).text();
// console.log("point will lie outside");
}
// }
})
})
console.log(this);
}
}
},
title: {
text: 'Historic World Population by Region'
},
subtitle: {
text: 'Source: Wikipedia.org'
},
xAxis: {
categories: ['Africa', 'America', 'Asia', 'Europe', 'Oceania'],
title: {
text: null
}
},
yAxis: {
min: 0,
title: {
text: 'Population (millions)',
align: 'high'
}
},
tooltip: {
formatter : function(){
return '<b>' +this.series.name + '<br/>' +this.x + '<br/>' + this.y+ '000000</b>'
}
},
plotOptions: {
series : {
allowPointSelect : true,
dataLabels: {
enabled : true,
color : "blue",
crop : true,
overflow: "none"
},
point:{
events : {
select :function() {
//get the selected object
pointSelected.categories = this.category;
pointSelected.y = this.y;
console.log(this);
changeOpacity(pointSelected);
},
mouseOver : function(){
//get the hovered object
pointHovered.categories = this.category;
pointHovered.y = this.y;
changeOpacityOnHover(pointHovered);
},
mouseOut : function(){
//event handler when mouse moves out
changeOpacityOnOut(pointHovered);
}
}
}
}
},
legend: {
align: 'right',
verticalAlign: 'top',
layout: 'vertical',
x: -150,
y: 100,
},
credits: {
enabled: false
},
series: [{
name: 'Year 1800',
data: [10700, 45000, 45000, 20300, 20000],
zones : [{value : 100 , color : 'orange'} , {value : 500 , color : 'black'} , { color : 'blue'}]
}]
}
//debugger
var barChart = new Highcharts.chart('container', chartObject);
//function on mouseOver
function changeOpacityOnHover(pointHovered){
//get the current chart object
var chart = $("#container").highcharts();
//get the points and check each point whether it is the hovered one
chart.series.forEach(function(obj){
obj.data.forEach(function(datum){
//if hovered one then get its graphic element(rect) and change its opacity to 1
if(datum.category == pointHovered.categories && datum.y == pointHovered.y ){
// console.log(datum) ;
// console.log(datum.graphic.element);
var tag = datum.graphic.element;
var x = $(tag).attr("x");
// console.log(x);
$(tag).css("opacity" , "1");
}
});
})
}
function changeOpacityOnOut(pointHovered){
//get the current chart object
var chart = $("#container").highcharts();
//get the points and check each point whether it is the hovered one from which mouse is over
chart.series.forEach(function(obj){
obj.data.forEach(function(datum){
//get its graphic element(rect)
if(datum.category == pointHovered.categories && datum.y == pointHovered.y ){
// console.log(datum) ;
// console.log(datum.graphic.element);
var tag = datum.graphic.element;
var x = $(tag).attr("x");
// console.log(x);
//if the current point(hovered) is selected one OR no point is yet selected , opacity will be 1
if((pointHovered.categories == pointSelected.categories && pointHovered.y== pointSelected.y) || Object.keys(pointSelected).length == 0 )
{
$(tag).css("opacity" , "1");
}
//else change opacity to 0.1
else{
$(tag).css("opacity" , "0.1");
}
}
});
})
}
// if point is selected
function changeOpacity(pointSelected){
//get the current chart object
var chart = $("#container").highcharts();
//get the selected point by comparing each point to pointSelected
chart.series.forEach(function(obj){
obj.data.forEach(function(datum){
// if current point is selected point then change opacity to 1 and its color to the color of its rect tag fill attribute
if(datum.category == pointSelected.categories && datum.y == pointSelected.y){
console.log(datum) ;
// console.log(datum.graphic.element);
var tag = datum.graphic.element;
//var xVal = datum.graphic.element.x.animVal.value;
//var yVal = datum.graphic.element.y.animVal.value
//console.log(xVal);
//console.log(yVal);
var x = $(tag).attr("x");
//console.log(x);
// var x2 = xVal -1;
// console.log(datum.dataLabel.translate(xVal , yVal - 1));
//console.log("after");
// console.log(x2);
//console.log(yVal-1);
$(tag).css("opacity" , "1");
var color = $(tag).attr("fill");
$(tag).css("fill" , color);
// console.log(color + "when clicked");
}
//else let its opacity be 0.1
else{
var tag = datum.graphic.element;
$(tag).css("opacity" , "0.1");
}
});
})
}
</script>
</body>
</html>
I have solved the problem using jQuery but if it can be done simply by setting property that will be great. Also , why the effect is not being reflected if the property is set??
Thanks.
It should work with your configuration, this is how dataLabels should be configured:
plotOptions: {
bar: {
dataLabels: {
enabled: true,
crop: true,
overflow: 'none',
// inside: true // by default it's outside the bar
}
}
},
Demo: http://jsfiddle.net/pbjpr47t/

Can I create multiple dynamic carts with Highcharts

First, sorry for my bad english.
I can create DBCont number charts in different div dynamicly. Like this:
$(function() {
$(document).ready(function() {
Highcharts.setOptions({
global: {
useUTC: false
}
});
for (var g = 0; g <= DbCount; g++)
{
var $container = $('<div id="div' + g + '" style="width: 400px; height: 200px;"></div>').appendTo(document.body);
new Highcharts.Chart({
chart: {
renderTo: $container[0],
type: 'areaspline',
marginRight: 10,
zoomType: 'x',
spacingRight: 20,
events: {
load: function() {
var series = this.series[0];
var series2 = this.series[1];
setInterval(function() {
var x = (new Date()).getTime(),
y1 = UsageGrafik[g],
y2 = UsageGrafik2[g];
if (y1 > 120 || y2 > 120 || isNaN(y1) || isNaN(y2)) {
y1 = 1;
y2 = 1;
}
series.addPoint([x, y1], false, true);
series2.addPoint([x, y2], true, true);
}, 5000);
....
You can see this is a dynamic chart.
My problem is setInterval(function()) sections are same for all charts. So all charts have same graphics. Because UsageGrafik[] array is change every 5 seconds in same page with ajax.
Basicly, i want create multiple charts with different setInterval(function()) section. Or another solution. Thank you
It's caused by closures. You have two options:
wrap creating chart into function: (function(g) { new Highcharts.Chart(...) })(g)
move out setInterval from definition:
new Highcharts.Chart({
chart: {
myIndex: g
renderTo: $container[0],
events: {
load: updateChart
}
},
...
});
function updateChart(e) {
var series = this.series[0];
var series2 = this.series[1];
var g = this.userOptions.chart.myIndex;
setInterval(function() {
var x = (new Date()).getTime(),
y1 = UsageGrafik[g],
y2 = UsageGrafik2[g];
if (y1 > 120 || y2 > 120 || isNaN(y1) || isNaN(y2)) {
y1 = 1;
y2 = 1;
}
series.addPoint([x, y1], false, true);
series2.addPoint([x, y2], true, true);
}, 5000);
....
}

OpenLayers: Rotate image with ModifyFeature as Polygon or other

I need to allow user to rotate bitmap features on map with OpenLayers.Control.ModifyFeature or another way as it work for Polygons or other geometry objects, except Point, but only for Point I can set "externalGraphic" with my bitmap. Example of ModifyFeature to rotation as I expected here: ModifyFeature example
When I add Vector with Point geometry and activate ModifyFeature there is no rotation tool showing - only drag-drop. I know what is a point, but I need to have tool for rotate bitmap features. It may be image on any another geometry object, but with custom image.
After long research I found an example in gis.stackexchange.com, and fix it.
Here is a code which works for me:
OpenLayers.Control.RotateGraphicFeature = OpenLayers.Class(OpenLayers.Control.ModifyFeature, {
rotateHandleStyle: null,
initialize: function(layer, options) {
OpenLayers.Control.ModifyFeature.prototype.initialize.apply(this, arguments);
this.mode = OpenLayers.Control.ModifyFeature.ROTATE; // This control can only be used to rotate the feature
this.geometryTypes = ['OpenLayers.Geometry.Point'] // This control can only be used to rotate point because the 'exteralGraphic' is a point style property
var init_style = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style.select);
this.rotateHandleStyle = OpenLayers.Util.extend(init_style, {
externalGraphic: "./static/resources/images/cross.png",
graphicWidth: 12,
graphicHeight: 12,
fillOpacity: 1
});
},
resetVertices: function() {
// You need to set yours renderIntent or use "vertex"
if (this.feature && this.feature.renderIntent == "vertex") {
var vertex = this.feature;
this.feature = this.backup_feature;
this.layer.destroyFeatures([this.radiusHandle], {silent: true});
this.collectRadiusHandle();
return;
}
if (this.dragControl.feature) {
this.dragControl.outFeature(this.dragControl.feature);
}
if (this.vertices.length > 0) {
this.layer.removeFeatures(this.vertices, {silent: true});
this.vertices = [];
}
if (this.virtualVertices.length > 0) {
this.layer.removeFeatures(this.virtualVertices, {silent: true});
this.virtualVertices = [];
}
if (this.dragHandle) {
this.layer.destroyFeatures([this.dragHandle], {silent: true});
this.dragHandle = null;
}
if (this.radiusHandle) {
this.layer.destroyFeatures([this.radiusHandle], {silent: true});
this.radiusHandle = null;
}
if (this.feature && this.feature.geometry &&
this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") {
if ((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) {
this.collectDragHandle();
}
if ((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE |
OpenLayers.Control.ModifyFeature.RESIZE))) {
this.collectRadiusHandle();
}
if (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE) {
if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)) {
this.collectVertices();
}
}
}
this.collectRadiusHandle();
},
collectRadiusHandle: function() {
var scope = this,
feature = this.feature,
geometry = this.feature.geometry || this.backup_feature.geometry,
centroid = geometry.getCentroid().transform(this.WGS84_google_mercator, this.WGS84),
lon = centroid.x, lat = centroid.y;
if (this.feature.geometry) {
this.backup_feature = this.feature;
} else {
this.feature.geometry = this.backup_feature.geometry;
}
var originGeometry = new OpenLayers.Geometry.Point(lon, lat);
// radius geometry position.
var pixel_dis_x = 10,
pixel_dis_y = -10;
var rotationFeatureGeometry = new OpenLayers.Geometry.Point(lon+pixel_dis_x, lat+pixel_dis_y);
var rotationFeature = new OpenLayers.Feature.Vector(rotationFeatureGeometry, null, this.rotateHandleStyle);
var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE);
var reshape = (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE);
var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE);
rotationFeatureGeometry.move = function(x, y) {
OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
var dx1 = this.x - originGeometry.x;
var dy1 = this.y - originGeometry.y;
var dx0 = dx1 - x;
var dy0 = dy1 - y;
if (rotate) {
var a0 = Math.atan2(dy0, dx0);
var a1 = Math.atan2(dy1, dx1);
var angle = a1 - a0;
angle *= 180 / Math.PI;
var old_angle = feature.attributes.angle;
var new_angle = old_angle - angle;
feature.attributes.angle = new_angle;
// redraw the feature
scope.feature.layer.redraw.call(scope.feature.layer);
}
};
rotationFeature._sketch = true;
this.radiusHandle = rotationFeature;
this.radiusHandle.renderIntent = this.vertexRenderIntent;
this.layer.addFeatures([this.radiusHandle], {silent: true});
},
CLASS_NAME: "OpenLayers.Control.RotateGraphicFeature"
});
Style of your Vector may be as:
new OpenLayers.StyleMap({
"default": new OpenLayers.Style({
externalGraphic: "link/to/icon",
graphicHeight: "32px",
graphicWidth: "25px",
fillOpacity: 1,
rotation: "${angle}",
graphicZIndex: 1
})
})
UPD: I fixed it for OpenLayers 2.13.1
OpenLayers.Control.RotateGraphicFeature = OpenLayers.Class(OpenLayers.Control.ModifyFeature, {
rotateHandleStyle: null,
initialize: function (layer, options) {
OpenLayers.Control.ModifyFeature.prototype.initialize.apply(this, arguments);
this.mode = OpenLayers.Control.ModifyFeature.ROTATE; // This control can only be used to rotate the feature
this.geometryTypes = ['OpenLayers.Geometry.Point'] // This control can only be used to rotate point because the 'exteralGraphic' is a point style property
var init_style = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style.select);
this.rotateHandleStyle = OpenLayers.Util.extend(init_style, {
externalGraphic: "./static/resources/images/cross.png",
graphicWidth: 12,
graphicHeight: 12,
fillOpacity: 1
});
},
resetVertices: function () {
// You need to set yours renderIntent or use "vertex"
if (this.feature && this.feature.renderIntent == "vertex") {
var vertex = this.feature;
this.feature = this.backup_feature;
if (this.dragControl.feature) {
this.dragControl.outFeature(this.dragControl.feature);
}
this.layer.destroyFeatures([this.radiusHandle], {silent: true});
delete this.radiusHandle;
this.collectRadiusHandle();
return;
}
if (this.vertices.length > 0) {
this.layer.removeFeatures(this.vertices, {silent: true});
this.vertices = [];
}
if (this.virtualVertices.length > 0) {
this.layer.removeFeatures(this.virtualVertices, {silent: true});
this.virtualVertices = [];
}
if (this.dragHandle) {
this.layer.destroyFeatures([this.dragHandle], {silent: true});
this.dragHandle = null;
}
if (this.radiusHandle) {
this.layer.destroyFeatures([this.radiusHandle], {silent: true});
this.radiusHandle = null;
}
if (this.feature && this.feature.geometry &&
this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") {
if ((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) {
this.collectDragHandle();
}
if ((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE |
OpenLayers.Control.ModifyFeature.RESIZE))) {
this.collectRadiusHandle();
}
if (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE) {
if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)) {
this.collectVertices();
}
}
}
this.collectRadiusHandle();
},
collectRadiusHandle: function () {
var scope = this,
feature = this.feature,
data = feature.attributes,
geometry = this.feature.geometry || this.backup_feature.geometry,
center = this.feature.geometry.bounds.getCenterLonLat();
centroid = geometry.getCentroid().transform(this.WGS84_google_mercator, this.WGS84),
lon = centroid.x, lat = centroid.y;
if (data.type && Tms.settings.roadObjectTypeSettings[data.type].NoAzimuth) {
return;
}
if (this.feature.geometry) {
this.backup_feature = this.feature;
} else {
this.feature.geometry = this.backup_feature.geometry;
}
var originGeometry = new OpenLayers.Geometry.Point(lon, lat);
var center_px = this.map.getPixelFromLonLat(center);
// you can change this two values to get best radius geometry position.
var pixel_dis_x = 20,
pixel_dis_y = 20;
var radius_px = center_px.add(pixel_dis_x, pixel_dis_y);
var rotation_lonlat = this.map.getLonLatFromPixel(radius_px);
var rotationFeatureGeometry = new OpenLayers.Geometry.Point(
rotation_lonlat.lon, rotation_lonlat.lat
);
var rotationFeature = new OpenLayers.Feature.Vector(rotationFeatureGeometry, null, this.rotateHandleStyle);
var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE);
var reshape = (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE);
var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE);
rotationFeatureGeometry.move = function(x, y) {
OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
var dx1 = this.x - originGeometry.x;
var dy1 = this.y - originGeometry.y;
var dx0 = dx1 - x;
var dy0 = dy1 - y;
if (rotate) {
var a0 = Math.atan2(dy0, dx0);
var a1 = Math.atan2(dy1, dx1);
var angle = a1 - a0;
angle *= 180 / Math.PI;
var old_angle = feature.attributes.angle;
var new_angle = old_angle - angle;
feature.attributes.angle = new_angle;
// redraw the feature
scope.feature.layer.redraw.call(scope.feature.layer);
}
};
rotationFeature._sketch = true;
this.radiusHandle = rotationFeature;
this.radiusHandle.renderIntent = this.vertexRenderIntent;
this.layer.addFeatures([this.radiusHandle], {silent: true});
},
CLASS_NAME: "OpenLayers.Control.RotateGraphicFeature"
});

Categories