I'm struggling to generate a simple XY horizontal bar chart in amCharts 4 using an external data source.
Everything works fine with static data (where I adapted one of the chart demos from the amCharts documentation).
But when I replace the static data with the external dataSource URL, the chart won't generate.
I suspect that the problem might be the Airtable JSON output is a different structure to the static data.
However, I could be completely wrong on that suspicion, so I'd really appreciate any help to a solution please.
(I've obviously XXX'd out the Airtable information in the code below.)
Static Data Source
// Create chart instance
var chart = am4core.create("CHARTDIV", am4charts.XYChart);
// Add data
chart.data = [{
"Name": "Brand Guidelines",
"Aggregate Responses": 7
}, {
"Name": "SAP",
"Aggregate Responses": 3
}, {
"Name": "Email",
"Aggregate Responses": 5
}, {
"Name": "Social Media",
"Aggregate Responses": 3
}, {
"Name": "Google Drive",
"Aggregate Responses": 3
}, {
"Name": "OneDrive",
"Aggregate Responses": 4
}, {
"Name": "SharePoint",
"Aggregate Responses": 1
}, {
"Name": "Slack",
"Aggregate Responses": 3
}, {
"Name": "Drupal",
"Aggregate Responses": 2
}, {
"Name": "Telephone",
"Aggregate Responses": 3
}];
// Create axes
var categoryAxis = chart.yAxes.push(new am4charts.CategoryAxis());
categoryAxis.dataFields.category = "Name";
categoryAxis.renderer.grid.template.location = 0;
categoryAxis.renderer.minGridDistance = 30;
var valueAxis = chart.xAxes.push(new am4charts.ValueAxis());
// Create series
var series = chart.series.push(new am4charts.ColumnSeries());
series.dataFields.valueX = "Aggregate Responses";
series.dataFields.categoryY = "Name";
series.name = "Aggregate Responses";
series.columns.template.tooltipText = "{categoryY}: [bold]{valueX}[/]";
series.columns.template.fillOpacity = .8;
var columnTemplate = series.columns.template;
columnTemplate.strokeWidth = 2;
columnTemplate.strokeOpacity = 1;
External Data Source
// Create chart instance
var chart = am4core.create("CHARTDIV", am4charts.XYChart);
// External data source
chart.dataSource.url = "https://api.airtable.com/v0/appXXXXXXX/airtable-table-name?api_key=keyXXXXXXX";
// Create axes
var categoryAxis = chart.yAxes.push(new am4charts.CategoryAxis());
categoryAxis.dataFields.category = "Name";
categoryAxis.renderer.grid.template.location = 0;
categoryAxis.renderer.minGridDistance = 30;
var valueAxis = chart.xAxes.push(new am4charts.ValueAxis());
// Create series
var series = chart.series.push(new am4charts.ColumnSeries());
series.dataFields.valueX = "Aggregate Responses";
series.dataFields.categoryY = "Name";
series.name = "Aggregate Responses";
series.columns.template.tooltipText = "{categoryY}: [bold]{valueX}[/]";
series.columns.template.fillOpacity = .8;
var columnTemplate = series.columns.template;
columnTemplate.strokeWidth = 2;
columnTemplate.strokeOpacity = 1;
Airtable JSON Output
{
"records": [
{
"id": "recXXXXXX",
"fields": {
"Name": "EpiServer",
"Typeform Responses": [
"recXXXXXX",
"recXXXXXX",
"recXXXXXX"
],
"Aggregate Responses": 3
},
"createdTime": "2020-05-22T16:11:01.000Z"
},
{
"id": "recXXXXXX",
"fields": {
"Name": "OneDrive",
"Typeform Responses": [
"recXXXXXX",
"recXXXXXX",
"recXXXXXX",
"recXXXXXX"
],
"Aggregate Responses": 4
},
"createdTime": "2020-05-22T16:50:41.000Z"
},
]
}
Your suspicion about the format being different is the right one. AmCharts needs a simple, flattened array of objects where it can find the fields you've specified, so you'll need to convert your external data into a format that AmCharts can accept. You can use the parseended event to re-map your data like so:
chart.dataSource.events.on("parseended", function(ev) {
// parsed data is assigned to data source's `data` property
// pluck data from the records array in your raw data
ev.target.data = ev.target.data.records.map(function(dataItem) {
return {
"Aggregate Responses": dataItem.fields["Aggregate Responses"],
"Name": dataItem.fields.Name
}
});
});
Example below using your code and a base64 encoded data URL of your sample data as demonstration:
// Create chart instance
var chart = am4core.create("CHARTDIV", am4charts.XYChart);
// External data source
//chart.dataSource.url = "https://api.airtable.com/v0/appXXXXXXX/airtable-table-name?api_key=keyXXXXXXX";
chart.dataSource.url = dataURI(); //fake URL for demonstration purposes
chart.dataSource.events.on("parseended", function(ev) {
// parsed data is assigned to data source's `data` property
ev.target.data = ev.target.data.records.map(function(dataItem) {
return {
"Aggregate Responses": dataItem.fields["Aggregate Responses"],
"Name": dataItem.fields.Name
}
});
});
// Create axes
var categoryAxis = chart.yAxes.push(new am4charts.CategoryAxis());
categoryAxis.dataFields.category = "Name";
categoryAxis.renderer.grid.template.location = 0;
categoryAxis.renderer.minGridDistance = 30;
var valueAxis = chart.xAxes.push(new am4charts.ValueAxis());
// Create series
var series = chart.series.push(new am4charts.ColumnSeries());
series.dataFields.valueX = "Aggregate Responses";
series.dataFields.categoryY = "Name";
series.name = "Aggregate Responses";
series.columns.template.tooltipText = "{categoryY}: [bold]{valueX}[/]";
series.columns.template.fillOpacity = .8;
var columnTemplate = series.columns.template;
columnTemplate.strokeWidth = 2;
columnTemplate.strokeOpacity = 1;
// base64 encoded version of sample data as a data URI
function dataURI() {
return "data:application/json;base64,eyAicmVjb3JkcyI6IFsgeyAiaWQiOiAicmVjWFhYWFhYIiwgImZpZWxkcyI6IHsgIk5hbWUiOiAiRXBpU2VydmVyIiwgIlR5cGVmb3JtIFJlc3BvbnNlcyI6IFsgInJlY1hYWFhYWCIsICJyZWNYWFhYWFgiLCAicmVjWFhYWFhYIiBdLCAiQWdncmVnYXRlIFJlc3BvbnNlcyI6IDMgfSwgImNyZWF0ZWRUaW1lIjogIjIwMjAtMDUtMjJUMTY6MTE6MDEuMDAwWiIgfSwgeyAiaWQiOiAicmVjWFhYWFhYIiwgImZpZWxkcyI6IHsgIk5hbWUiOiAiT25lRHJpdmUiLCAiVHlwZWZvcm0gUmVzcG9uc2VzIjogWyAicmVjWFhYWFhYIiwgInJlY1hYWFhYWCIsICJyZWNYWFhYWFgiLCAicmVjWFhYWFhYIiBdLCAiQWdncmVnYXRlIFJlc3BvbnNlcyI6IDQgfSwgImNyZWF0ZWRUaW1lIjogIjIwMjAtMDUtMjJUMTY6NTA6NDEuMDAwWiIgfSBdIH0=";
}
<script src="//www.amcharts.com/lib/4/core.js"></script>
<script src="//www.amcharts.com/lib/4/charts.js"></script>
<div id="CHARTDIV" style="width: 100%; height: 98vh"></div>
Make sure your data is valid JSON as your sample has an incorrect trailing comma.
I am working with chartjs, I am trying to animate chart from right to left or left to right on load.
var canvas = document.getElementById('chart_canvas');
var ctx = canvas.getContext('2d');
// Generate random data to plot
var DATA_POINT_NUM = 10;
var data = {
labels: [],
datasets: [
{
data: [],
},
]
}
for (var i=0; i<DATA_POINT_NUM; i++) {
data.datasets[0].data.push(Math.random()*10);
data.labels.push(String.fromCharCode(65+i));
}
var oldDraw = Chart.controllers.line.prototype.draw;
Chart.controllers.line.prototype.draw = function(animationFraction) {
var animationConfig = this.chart.options.animation;
if (animationConfig.xAxis === true) {
var ctx = this.chart.chart.ctx;
var hShift = (1-animationFraction)*ctx.canvas.width;
ctx.save();
ctx.setTransform(1, 0, 0, 1, hShift,0);
if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
ctx.restore();
} else if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
}
var lineChart = new Chart(ctx, {
type: 'line',
data: data,
options: {
animation: {
duration: 5000,
xAxis: true,
yAxis: true,
}
}
});
Example 1
The above code works fine on windows, but I'm facing issue on mac devices.While animating from left to right the data displays incorrectly means that the data moves to upward from x axis.How to fix this issue?
I am attaching screenshot.
Screenshot
Please change setTransform to transform.
Try the following code
var canvas = document.getElementById('chart_canvas');
var ctx = canvas.getContext('2d');
// Generate random data to plot
var DATA_POINT_NUM = 10;
var data = {
labels: [],
datasets: [
{
data: [],
},
]
}
for (var i=0; i<DATA_POINT_NUM; i++) {
data.datasets[0].data.push(Math.random()*10);
data.labels.push(String.fromCharCode(65+i));
}
var oldDraw = Chart.controllers.line.prototype.draw;
Chart.controllers.line.prototype.draw = function(animationFraction) {
var animationConfig = this.chart.options.animation;
if (animationConfig.xAxis === true) {
var ctx = this.chart.chart.ctx;
var hShift = (1-animationFraction)*ctx.canvas.width;
ctx.save();
ctx.transform(1, 0, 0, 1, hShift,0);
if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
ctx.restore();
} else if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
}
var lineChart = new Chart(ctx, {
type: 'line',
data: data,
options: {
animation: {
duration: 5000,
xAxis: true,
yAxis: true,
}
}
});
I am creating animations of migrating sea animals across the Pacific using OpenLayers. I would like each "animal" to trace a track as it goes over time. At the head of the track will be an icon/marker/overlay representing that animal. I have gotten this to work for one track, but although I am able to grow the track of each animal as a linestring constructed segment by segment, I am unable to specifically assign an icon/marker/overlay to each track. Instead, I am only able to animate one icon on one track. The rest of the linestring tracks proceed, but they do not have an icon at the head of the track as it is traced out. Here is my code. Any help appreciated.
// draw tracks
function makeLineString(id, species, multipointCoords, tracksTime) {
// layer structure and style assignment
var trackSource = new ol.source.Vector();
var trackLayer = new ol.layer.Vector({
source: trackSource,
style: BWtrackStyle
});
map.addLayer(trackLayer);
var lineString = new ol.geom.LineString([
ol.proj.fromLonLat(multipointCoords[0][0])
]);
var trackFeature = new ol.Feature({
geometry: lineString
});
if (species === "Blue Whale") {
trackFeature.setStyle([BWtrackStyle, shadowStyle]);
};
trackSource.addFeature(trackFeature);
// icon-marker-overlay styling
var BW2205005icon = document.getElementById('BW2205005icon');
var BW2205005marker = new ol.Overlay({
positioning: 'center-center',
offset: [0, 0],
element: BW2205005icon,
stopEvent: false
});
map.addOverlay(BW2205005marker);
var BW2205012icon = document.getElementById('BW2205012icon');
var BW2205012marker = new ol.Overlay({
positioning: 'center-center',
offset: [0, 0],
element: BW2205012icon,
stopEvent: false
});
map.addOverlay(BW2205012marker);
var coordinate, i = 1,
length = multipointCoords[0].length;
var currentTime = tracksTime[0][0];
var nextTime = tracksTime[0][1];
speedOption = 100; // the highter this value, the faster the tracks, see next line
var transitionTime = (nextTime - currentTime) / speedOption;
console.log(transitionTime);
var timer;
timer = setInterval(function() {
segmentConstruction(id, multipointCoords, tracksTime);
}, transitionTime);
function segmentConstruction(id, multipointCoords, tracksTime) {
coordinate = ol.proj.fromLonLat(multipointCoords[0][i]);
lineString.appendCoordinate(coordinate);
console.log(id);
if (id === "BW2205005") {
BW2205005marker.setPosition(coordinate);
} else {
BW2205012marker.setPosition(coordinate);
};
if (i >= length - 1) {
clearInterval(timer);
} else {
i++;
clearInterval(timer);
currentTime = tracksTime[0][i];
nextTime = tracksTime[0][i + 1];
transitionTime = (nextTime - currentTime) / speedOption;
timer = setInterval(function() {
segmentConstruction(id, multipointCoords, tracksTime);
}, transitionTime);
};
};
};
I would suggest this for animation of multiple markers with smooth movement, but it requires a delay for other styles:
var vehicles= [{ "curlon":77.654397, "curlat":12.959898, "prevlon":77.651951, "prevlat":12.951074 },{ "curlon":77.672936, "curlat":12.958100, "prevlon":77.649290, "prevlat":12.960024 }];
function push_data_for_multimarker(map,gps_obj)
{
destruct=false;
var getz = gps_obj;
var data_arry = [];
for (var i = 0, length = getz.length; i < length; i++)
{
gps_smooth_coordinates_generator(parseFloat(getz[i].prevlon),parseFloat(getz[i].prevlat),parseFloat(getz[i].curlon),parseFloat(getz[i].curlat));
}
}
function gps_smooth_coordinates_generator(map,sourcelon,sourcelat,destinationlon,destinationlat)
{
var vehicle_data=[];
var beginz=ol.proj.transform([sourcelon,sourcelat], 'EPSG:4326', 'EPSG:3857');
var endz=ol.proj.transform([destinationlon,destinationlat], 'EPSG:4326', 'EPSG:3857');
vehicle_data.push(beginz);
vehicle_data.push(endz);
var path= vehicle_data;
var genrated_positions = [];
for(var k = 1;k<path.length;k++)
{
var pointsNo = 1000;
var startPos = {};
startPos.lat = path[k-1][1];
startPos.lng = path[k-1][0];
var endPos = {};
endPos.lat = path[k][1];
endPos.lng = path[k][0];
var latDelta = (endPos.lat - startPos.lat) / pointsNo;
var lngDelta = (endPos.lng - startPos.lng) / pointsNo;
for (var i = 0; i < pointsNo; i++)
{
var curLat = startPos.lat + i * latDelta;
var curLng = startPos.lng + i * lngDelta;
var arr = [];
arr.push(curLng);
arr.push(curLat);
genrated_positions.push(arr);
}
}
animate_multiple_marker(genrated_positions);
}
function animate_multiple_marker(map,veh_croods)
{
var routeCoords = veh_croods;
var routeLength = routeCoords.length;
console.log("routeCoords"+routeCoords);
console.log("routeLength"+routeLength);
var geoMarker = new ol.Feature({
type: 'geoMarker',
geometry: new ol.geom.Point(routeCoords[0])
});
var off_style =new ol.style.Style({
image: new ol.style.Circle({
radius: 7,
snapToPixel: false,
fill: new ol.style.Fill({color: 'black'}),
stroke: new ol.style.Stroke({
color: 'white', width: 2
})
})
});
var now;
var animating = false;
var speed=20;
var moveFeature = function(event) {
var vectorContext = event.vectorContext;
var frameState = event.frameState;
if (animating) {
var elapsedTime = frameState.time - now;
var index = Math.round(speed * elapsedTime / 1000);
if (index >= routeLength) {
console.log("index>>"+index);
stopAnimation();
animating = false;
console.log("animation ends");
}
if(animating)
{
var currentPoint = new ol.geom.Point(routeCoords[index]);
var feature = new ol.Feature(currentPoint);
vectorContext.drawFeature(feature,off_style);
}
else
{}
if(destruct)
{
console.log("termination initiated");
stopAnimation();
//destruct=false;
}
else
{ }
}
else
{
console.log("Not amnimating!!");
}
// tell OL3 to continue the postcompose animation
map.render();
};
triggerz_animation();
function stopAnimation() {
animating = false;
caller=false;
//remove listener
map.un('postcompose', moveFeature);
}
function start_vehicles()
{
animating = true;
now = new Date().getTime();
map.on('postcompose', moveFeature);
map.render();
}
if(caller)
{
start_vehicles();
}
else
{
}
}
var caller=false;
var drive_vehicle;
function triggerz_animation()
{
caller=true;
}
var destruct=false;
function collapse_animation()
{
destruct=true;
}
with reference to
http://openlayers.org/en/latest/examples/feature-move-animation.html?q=animation
Let this be helpfull..Thank You
pass data as json to the function:
push_data_for_multimarker(map,vehicles);
I think your overlay is being reused for each line. In any case, it would probably be simpler and more performant to use a point feature instead of the overlay, as follows:
add a new style to your layer for points (circle, image, etc.)
add a point feature representing the head of each line
update the coordinates of the point feature when necessary
In other words, each animal would have a linestring and a point feature. The style on the layer would include the stroke style for the line and the icon style for the point.
You could also style the points independently by giving each point feature it's own style, or using a style function on the layer.
I'm facing a performance problem with Leaflet (version 0.7.3). I'm working with an OSM map that I use to display a bunch of CircleMarkers linked by decorated Polylines (with arrow pattern every 25px). Loading take a little time but the main problem is that when I zoom the map I start facing severe lag (from the zoom level 16) and, beyond a certain limit (say 18 most of the time), browser just freeze and eventually crash (tested with chrome and firefox). I tried with a bunch of 1,000 linked markers, then I dropped to a set of around 100, but still the same concern... Of course, with 10 markers or less I don't have any problem.
Did you already face a similar trouble? How can I optimize Leaflet performances so that I can use an accurate zoom (beyond level 16) with more than 100 linked CircleMarkers ? I also wonder why performances are dropping so badly when zooming, while marker amount stay the same...
Thank you in advance for your answers,
Lenalys.
Cannot get the PolylineDecorator plugin to work on jsfiddle.
But here is the code that generate markers :
Map initialization :
var map;
function initializeMap(){
"use strict";
var layer;
var layer2;
function layerUrl(key, layer) {
return "http://wxs.ign.fr/" + key
+ "/geoportail/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&"
+ "LAYER=" + layer + "&STYLE=normal&TILEMATRIXSET=PM&"
+ "TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image%2Fjpeg";
}
layer = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png',
{
attribution: '© OpenStreetMap contributors',
maxZoom: 18
});
layer2 = L.tileLayer(
layerUrl(IGN_AMBIENTE_KEY, "GEOGRAPHICALGRIDSYSTEMS.MAPS"),
{attribution: '© IGN'}
);
var baseMaps = {
"Terrestre": layer,
"Bathymetrique": layer2
};
map = L.map('map', {
layers: [layer],
zoom: 8,
center: [42.152796, 9.139150],
zoomControl: false
});
L.control.layers(baseMaps).addTo(map);
//add zoom control with your options
L.control.zoom({
position:'topright' //topleft
}).addTo(map);
L.control.scale({
position:'bottomleft',
imperial : false
}).addTo(map);
}
Data Sample :
var jsonData ={"12":[{"id_stm_device":"7","individual_name":"cerf3","latitude":"42.657283333333","longitude":"9.42362","temperature":null,"pulse":null,"battery":"20","date_time":"2015-03-17 15:37:12"},
{"id_stm_device":"7","individual_name":"cerf3","latitude":"42.657381666667","longitude":"9.42365","temperature":null,"pulse":null,"battery":"20","date_time":"2015-03-17 16:42:16"},
{"id_stm_device":"7","individual_name":"cerf3","latitude":"42.657381666667","longitude":"9.4236933333333","temperature":null,"pulse":null,"battery":"20","date_time":"2015-03-17 17:47:21"},
{"id_stm_device":"7","individual_name":"cerf3","latitude":"42.657283333333","longitude":"9.4237383333333","temperature":null,"pulse":null,"battery":"20","date_time":"2015-03-17 19:57:23"}],
"13":[{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.61683","longitude":"9.4804633333333","temperature":"17.45","pulse":null,"battery":"80","date_time":"2015-04-08 07:45:20"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.538858333333","longitude":"9.48169","temperature":"14.37","pulse":null,"battery":"80","date_time":"2015-04-08 08:00:29"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.458748333333","longitude":"9.500225","temperature":"14.46","pulse":null,"battery":"80","date_time":"2015-04-08 08:15:49"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.3302","longitude":"9.5374583333333","temperature":"15.19","pulse":null,"battery":"80","date_time":"2015-04-08 08:31:05"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.170133333333","longitude":"9.5272116666667","temperature":"15.48","pulse":null,"battery":"80","date_time":"2015-04-08 08:46:20"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.07959","longitude":"9.47688","temperature":"15.97","pulse":null,"battery":"80","date_time":"2015-04-08 09:01:31"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.076163333333","longitude":"9.4828633333333","temperature":"20.42","pulse":null,"battery":"80","date_time":"2015-04-08 09:16:59"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.07194","longitude":"9.4908866666667","temperature":"17.36","pulse":null,"battery":"80","date_time":"2015-04-08 09:32:17"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.072583333333","longitude":"9.4901516666667","temperature":"17.36","pulse":null,"battery":"80","date_time":"2015-04-08 09:47:32"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.07238","longitude":"9.4904266666667","temperature":"19.38","pulse":null,"battery":"80","date_time":"2015-04-08 10:02:42"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.072298333333","longitude":"9.4904983333333","temperature":"17.46","pulse":null,"battery":"80","date_time":"2015-04-08 10:17:55"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.095093333333","longitude":"9.5148383333333","temperature":"17.47","pulse":null,"battery":"80","date_time":"2015-04-08 10:33:12"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.112881666667","longitude":"9.5133133333333","temperature":"19.3","pulse":null,"battery":"80","date_time":"2015-04-08 10:48:23"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.112875","longitude":"9.513285","temperature":"22.71","pulse":null,"battery":"80","date_time":"2015-04-08 11:03:57"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.141096666667","longitude":"9.5078216666667","temperature":"23.73","pulse":null,"battery":"80","date_time":"2015-04-08 11:19:12"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.282186666667","longitude":"9.5505183333333","temperature":"18.97","pulse":null,"battery":"80","date_time":"2015-04-08 11:34:28"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.405126666667","longitude":"9.531145","temperature":"20.71","pulse":null,"battery":"80","date_time":"2015-04-08 11:49:42"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.482063333333","longitude":"9.480665","temperature":"21.7","pulse":null,"battery":"80","date_time":"2015-04-08 12:05:07"}]}
var oJSON = JSON.parse(jsonData);
var colors = [
"#400080",
"#008000",
"#EC7600",
"#E40341",
"#0D5E5E",
"#919191",
"#FF3C9D",
"#A70A0E",
"#00BFBF",
"#7171FF"
];
var classes = [
"color1",
"color2",
"color3",
"color4",
"color5",
"color6",
"color7",
"color8",
"color9",
"color10"
];
var lastMarkers = [];
var layers = new Array();
var polyline;
var decorator;
window.graphicsDevices = [];
var offsetLatitude = 0.003333;
var offsetLongitude = 0.011666;
Marker instanciation :
function printGPSOnMap(oJSON){
var nbKeys = 0;
for (var key in oJSON) {
nbKeys++;
var classe = classes[(key-1)%classes.length];
var color = colors[(key-1)%colors.length];
var positionInfo = [];
if (oJSON.hasOwnProperty(key)) {
var aInfo = oJSON[key];
var marker;
var latlngs = Array();
var startMarker = lastMarkers[key];
if(startMarker !== undefined && startMarker != null) {
var myIcon = L.divIcon({className: "myCircle "+classe, iconSize : [ 20, 20 ] });
startMarker.setIcon(myIcon);
latlngs.push(startMarker.getLatLng());
}
for(var i = 0; i < aInfo.length; i++) {
var oInfos = aInfo[i];
var sIdIndividual = oInfos["id_individual"];
var sLongitude = oInfos["longitude"];
var sLatitude = oInfos["latitude"];
var sTemperature = oInfos["temperature"];
var sPulse = oInfos["pulse"];
var sBattery = oInfos["battery"];
var sDatetime = oInfos["date_time"];
var sIndividualName = oInfos["individual_name"];
var id_device = oInfos["id_stm_device"];
var popupMsg = "...";
latlngs.push(L.marker([sLatitude,sLongitude]).getLatLng());
marker = new MyCustomMarker([sLatitude,sLongitude], {
icon : L.divIcon({
className : "myCircle "+classe + ((i == aInfo.length-1) ? ' myCircleEnd' : ''),
iconSize : [ 20, 20 ]
})
});
marker.bindPopup(popupMsg, {
showOnMouseOver: true
});
marker.bindLabel(key, {
noHide: true,
direction: 'middle',
offset: [offset[0], offset[1]]
});
positionInfo.push(marker);
}
lastMarkers[key] = marker;
}
if(latlngs.length > 1)
{
polyline = L.polyline(latlngs, {className: classe, weight: 2,opacity: 0.4}).addTo(map);
decorator = L.polylineDecorator(polyline, {
patterns: [
// define a pattern of 10px-wide arrows, repeated every 20px on the line
{offset: 0, repeat: '25px', symbol: new L.Symbol.arrowHead({pixelSize: 10, pathOptions: {fillOpacity:
0.76, color: color, weight: 1}})}
]}).addTo(map);
}
if(!window.graphicsDevices.hasOwnProperty(key))
window.graphicsDevices[key] = [];
for(var i = 0; i < positionInfo.length; i++) {
window.graphicsDevices[key].push(positionInfo[i]);
positionInfo[i].addTo(map);
if(latlngs.length > 1){
window.graphicsDevices[key].push(polyline);
polyline.addTo(map);
window.graphicsDevices[key].push(decorator);
decorator.addTo(map);
}
}
}//foreach key
}
Code for the custom marker :
var MyCustomMarker = L.Marker.extend({
bindPopup: function(htmlContent, options) {
if (options && options.showOnMouseOver) {
// call the super method
L.Marker.prototype.bindPopup.apply(this, [htmlContent, options]);
// unbind the click event
this.off("click", this.openPopup, this);
// bind to mouse over
this.on("mouseover", function(e) {
// get the element that the mouse hovered onto
var target = e.originalEvent.fromElement || e.originalEvent.relatedTarget;
var parent = this._getParent(target, "leaflet-popup");
// check to see if the element is a popup, and if it is this marker's popup
if (parent == this._popup._container)
return true;
// show the popup
this.openPopup();
}, this);
// and mouse out
this.on("mouseout", function(e) {
// get the element that the mouse hovered onto
var target = e.originalEvent.toElement || e.originalEvent.relatedTarget;
// check to see if the element is a popup
if (this._getParent(target, "leaflet-popup")) {
L.DomEvent.on(this._popup._container, "mouseout", this._popupMouseOut, this);
return true;
}
// hide the popup
this.closePopup();
}, this);
}
},
_popupMouseOut: function(e) {
// detach the event
L.DomEvent.off(this._popup, "mouseout", this._popupMouseOut, this);
// get the element that the mouse hovered onto
var target = e.toElement || e.relatedTarget;
// check to see if the element is a popup
if (this._getParent(target, "leaflet-popup"))
return true;
// check to see if the marker was hovered back onto
if (target == this._icon)
return true;
// hide the popup
this.closePopup();
},
_getParent: function(element, className) {
var parent = null;
if(element != null) parent = element.parentNode;
while (parent != null) {
if (parent.className && L.DomUtil.hasClass(parent, className))
return parent;
parent = parent.parentNode;
}
return false;
}
});
Have you gauged performance in canvas mode?
Use L_PREFER_CANVAS = true before initializing your leaflet map container.Might be able to help you possibly.
Hi I am trying to implement a combined highchart using dotnet highcharts.So I have a column chart+pie chart.
I made List allSeries = new List()for column Chart and List pieSeries = new List()for pie chart.
I dont know how to pass this two series to to the .SetSeries() wich accepts SetSeries(Series series);
or SetSeries(Series[] seriesArray);
public ActionResult Profit()
{
DBContext.Current.Open();
List<RoomType> result = new List<RoomType>();
result = RoomType.Selectcount();
List<Series> allSeries = new List<Series>();
List<Series> pieSeries = new List<Series>();
List<DotNet.Highcharts.Options.Point> puncte = new List<DotNet.Highcharts.Options.Point>();
string[] categories = new[] { "Ianuarie", "Februarie", "Martie", "Aprilie", "Mai", "Iunie", "Iulie", "August", "Septembrie", "Octombrie", "Noiembrie", "Decembrie" };
object[] pointnum = new object[12];
foreach (var j in result)
{
for (int i = 0; i < pointnum.Length; i++)
{
pointnum[i] = Roomtypereservations.RoomTypeByDate(j.RoomType_ID, i + 1).FirstOrDefault().NumRezervari;
}
allSeries.Add(new Series
{
Type=ChartTypes.Column,
Name = j.Room_Type,
//Data = new Data(myData)
Data = new Data(pointnum.ToArray())
});
pieSeries.Add(new Series
{
Type = ChartTypes.Pie,
Name = "Total rooms",
Data = new Data(puncte.ToArray())
});
puncte.Add(new DotNet.Highcharts.Options.Point
{
Name = j.Room_Type,
Y=13
//Data = new Data(myData)
});
}
Highcharts chart = new Highcharts("chart")
.SetTitle(new Title { Text = "Combination chart" })
.SetTooltip(new Tooltip { Formatter = "function() { return '<b>'+ this.point.name +'</b>: '+ this.percentage +' %'; }" })
.SetXAxis(new XAxis { Categories =categories} )
.SetTooltip(new Tooltip { Formatter = "TooltipFormatter" })
.AddJavascripFunction("TooltipFormatter",
#"var s;
if (this.point.name) { // the pie chart
s = ''+
this.point.name +': '+ this.y +' fruits';
} else {
s = ''+
this.x +': '+ this.y;
}
return s;")
.SetLabels(new Labels
{
Items = new[]
{
new LabelsItems
{
Html = "Total fruit consumption",
Style = "left: '40px', top: '8px', color: 'black'"
}
}
})
.SetPlotOptions(new PlotOptions
{
Pie = new PlotOptionsPie
{
Center = new[] { "100", "80" },
Size = "100",
ShowInLegend = false,
DataLabels = new PlotOptionsPieDataLabels { Enabled = false }
}
})
.SetSeries(allSeries.Select(s => new Series { Type = s.Type, Name = s.Name, Data = s.Data }).ToArray());
return View(chart);
When i am working with only one series like in my sample
its working:
.SetSeries(allSeries.Select(s => new Series { Type = s.Type, Name = s.Name, Data = s.Data }).ToArray());
how can i pas both pieSeries and all Series to .SetSeries?
You don't need both allSeries and pieSeries. I would get rid of pieSeries. You can assign as many series to your allSeries List as you need and they can be of any type. So change your pieSeries.Add to the following:
allSeries.Add(new Series
{
Type = ChartTypes.Pie,
Name = "Total rooms",
Data = new Data(puncte.ToArray())
})
Then the following statement will work and all of your required Series to the chart:
.SetSeries(allSeries.Select(s => new Series { Type = s.Type, Name = s.Name, Data = s.Data }).ToArray());