I want to get the data points which cannot be plotted on the underlying map (i.e. joinBy fails to map the data to the geojson). Is there any way to get the unplotted data?
You can check all points and find which are not plotted, the condition is that point has a value but doesn't have graphic:
chart: {
events: {
load: function () {
var chart = this,
unplottedPoints = [];
$.each(chart.series[0].data, function (i, point) {
if (point.value && !point.graphic) {
unplottedPoints.push(point);
}
});
console.log(unplottedPoints);
}
}
},
In array unplottedPoints you have list of all not rendered points.
Demo: http://jsfiddle.net/spmx9xu3/1/
Related
I am not being able to update min/max/avg values in Highcharts when doing zoom.
I am trying to combine these two pieces of code:
1.Update min/max/avg---->http://jsfiddle.net/d_paul/supgh9c1/4/
2.load DB tables based on zoom ---> https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/stock/demo/lazy-loading/
So the code shown here produce a "callback is not a function".
But if i remove the "e"(that is the event that highcharts pass when the user select a zoom) in the function after_Set_Extreme , I get "e" is not defined in that function. And if I add "e" as passing parameter (e,updateLegendLabel) I get same error as before, but now in the afterSetExtremes line. So it seems e(event) is not being recognized.
But if you see the link # 2 above, afterSetExtremes is called without passing "e", but is expected in the function . See function definition..
Can anyone help me to understand what is wrong here?
This is highcharts when doing the actual chart
$('#container2').highcharts('StockChart', {
chart: {
zoomType: 'x',
events: {
load: updateLegendLabel
}
},
......
xAxis: {
events: {
afterSetExtremes: after_Set_Extremes(updateLegendLabel),
},
.....
function after_Set_Extremes(e, callback) {
processed_json_temperatura.length = 0;
processed_json_presion.length = 0;
processed_json_humedad.length = 0;
processed_json_lluvia.length = 0;
processed_json_horas_frio.length = 0;
if (typeof(e.rangeSelectorButton) !== 'undefined') {
........
callback();
}
function updateLegendLabel() {
var chrt = !this.chart ? this : this.chart;
// alert('entre updatelegend')
console.log(this);
chrt.update({
....
}
Combining these two functionalities seems to be very simple, please check this example: https://jsfiddle.net/BlackLabel/oh3ubdLv/
However, in your case after_Set_Extremes function is called only once, when the chart is initialized. You need to create an additional function to pass arguments:
Highcharts.stockChart('container', {
...,
xAxis: {
events: {
afterSetExtremes: function(e) {
after_Set_Extremes(e, updateLegendLabel)
}
}
}
});
function after_Set_Extremes(e, callback) {
callback();
}
function updateLegendLabel() {
console.log('callback');
}
Live demo: http://jsfiddle.net/BlackLabel/ebdtry20/
Everytime I run this loop, each marker in the markers array has it's icon overwritten by the results from let icon = iconLoader.getIcon(data[index][5]);.
leaving each marker having the last loaded icon, instead of the icon loading during each pass of the for loop.
I thought that passing icon into the closure would essentially pass it by value, preventing it from being overwritten outside the scope of the closure, but this doesn't seem to be working for me. What am I missing?
var markers = []
for (var index in data) {
let icon = iconLoader.getIcon(data[index][5]);
var createMarker = (function (i) {
return function () {
var marker = new L.marker([data[index][2], data[index][3]])
.setIcon(i)
markers.push(marker);
}
})(icon);
createMarker();
}
var iconLoader = (function () {
var icon = L.icon({
// options
});
return {
getIcon: function (iconName) {
// do stuff to get icon
return icon;
}
};
}());
JSFiddle
So, as I mentioned in my original comment, JavaScript objects and arrays are always passed by reference unless you explicitly create and pass a copy. Nothing in your code is inherently wrong and is not causing this issue - it is actually an issue with how leaflet is handling the object references internally. The way to avoid this is to do a deep copy on the result from iconLoader.getIcon(). If you are using jQuery, you can do it very simply by using $.extend().
for (var index in data) {
let icon = $.extend(true, {}, iconLoader.getIcon(data[index][2]));
var marker = new L.marker([data[index][0], data[index][1]])
.setIcon(icon);
markers.push(marker);
}
If not, you can look into non-jQuery solutions - it's not ideal, but they're everywhere.
I was typing this up as mhodges was writing his answer. I'll go ahead and post it as it's a different solution that also solved the issue for me.
After looking at mhodges' working demo, I noticed he had set up the iconloader a bit differently than I had. He had the iconloader as an object...
var iconLoader = {
getIcon: function (elem) {
return elem;
}
}
while mine was set up as a closure...
var iconLoader = (function () {
var icon = L.icon({
// options
});
return {
getIcon: function (iconName) {
// do stuff to get icon
return icon;
}
};
}());
I thought that maybe I could try setting it up a bit differently and see if that made a difference, and VOILA!
const proto = {
getIcon (iconName) {
var icon = L.icon({
// options
});
// do stuff to get icon
return icon;
}
};
function factoryIcon() {
return Object.create(proto);
}
and then grabbed the icon with
const iconFactory = factoryIcon();
let icon = iconFactory.getIcon(data[index][5]);
I have a Web App which uses Google Charts.
There are more than one chart on a page.
I successfully create and render the charts.
Depending on the user's filters, I receive new chart data via Ajax.
How can I reacquire a chart object and update it, if I don't keep the returned object that far in the code?
I wonna do something similar to the following:
function DrawChart()
{
// Code code code ... more code
// Initialize
var chart = new google.visualization.ScatterChart(document.getElementById("my-chart-div"));
// Draw
chart.draw(data, options);
}
And later on:
function UserDidSomething()
{
var newData = MyAjaxCall(...);
var options = ...;
var chart = ...; // What goes here??
chart.draw(newData, options);
}
Thanks in advance,
Shy.
I created a dynamic charts object that keeps the created charts:
/// <summary>
/// This object holds created charts in order to edit them.
/// The key for the chart is the div id (e.g. charts["chart-my-chartname"]).
/// </summary>
var charts = {};
function ChartCreated(divId)
{
return charts[divId] !== undefined && charts[divId] != null;
}
function GetChart(divId)
{
return charts[divId];
}
function AddChart(divId, chart)
{
charts[divId] = chart;
}
function RemoveChart(divId)
{
charts[divId] = null;
}
function CreateOrUpdateChart(divId, chartType, data, options)
{
var chart;
// If the chart was previously created, use its object
if (ChartCreated(divId))
{
chart = GetChart(divId);
}
else // If there was no chart, create and keep it
{
chart = InitializeNewChart(chartType, divId);
AddChart(divId, chart);
}
// Create a new DataTable object using the JavaScript Literal Initializer, and the received JSON data object
data = new google.visualization.DataTable(data);
// Render chart
chart.draw(data, options);
}
function InitializeNewChart(type, divId)
{
var container = document.getElementById(divId);
switch (type)
{
case "Scatter": return new google.visualization.ScatterChart(container);
case "Column": return new google.visualization.ColumnChart(container);
case "Line": return new google.visualization.LineChart(container);
default: return null;
}
}
I need to iterate over an AJAX response and break out of an event handler when a condition is met. I'm having trouble with this code:
$.each(response, function(i, v) {
// create mapbox object
var map = L.mapbox.map('map', v.map_embed_id, {
zoomAnimation: false
});
var polygonLayer = L.mapbox.featureLayer().loadURL('https://a.tiles.mapbox.com/v4/' + v.map_embed_id + '/features.json?access_token=abcde').addTo(map);
polygonLayer.on('ready', function() {
var layer = leafletPip.pointInLayer(latlng, polygonLayer, true);
if (layer.length) {
// this is where I need to break out of $.on
// and the current $.each iteration
}
});
});
I know return false would break out of the $.each iteration but this is more difficult since I need to break out of the $.on event handler. What can I do? Could I use a trigger maybe?
Thanks to #Kevin B's advice to use recursion, this is how I fixed my code to make it work.
getMapsList().done(function(maps) {
getMapboxMap(maps, geocode);
});
function getMapboxMap(maps, geocode) {
var map_params = maps[0];
var map_embed_id = map_params.map_embed_id;
if (maps.length > 0)
maps.shift();
// create mapbox object
var map = L.mapbox.map('map', map_embed_id, {
zoomAnimation: false
});
// create marker of address entered
L.mapbox.featureLayer({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [
geocode.location.lng,
geocode.location.lat
]
},
properties: {
title: address,
'marker-size': 'medium',
'marker-color': '#f44',
'marker-symbol': 'star'
}
}).addTo(map);
// create polygon layer and add to map from map's geojson
var polygonLayer = L.mapbox.featureLayer().loadURL('https://a.tiles.mapbox.com/v4/' + map_embed_id + '/features.json?access_token=pk.eyJ1IjoiZW5nbGVzaWRldGVycml0b3JpZXMiLCJhIjoiekFIU0NlayJ9.rE9XdicgXc9aIiXJ9yn68w').addTo(map);
// after polygon layer has been added to map
polygonLayer.on('ready', function() {
// featureLayer.getBounds() returns the corners of the furthest-out markers,
// and map.fitBounds() makes sure that the map contains these.
map.fitBounds(polygonLayer.getBounds());
// create a latLng object based on lat/lng of address entered
var latlng = L.latLng(geocode.location.lat, geocode.location.lng);
// create point in layer object
var layer = leafletPip.pointInLayer(latlng, polygonLayer, true);
if (layer.length) {
// found it
return false;
} else {
if (maps.length > 0) {
getMapboxMap(maps, geocode);
}
}
});
}
function getMapsList() {
return $.get('/utility/territories/maps-list');
}
I used the animation for symbols on polylines according to this answer that was very useful:
Animate symbol on multiple geodesic polylines
What i would like is to have several polylines and generate animation when one is selected and stop animation for the others.
That is, i want to remove symbol and stop animation once it has started with the method mentioned above:
function animateCircle(id) {
var count = 0;
offsetId = window.setInterval(function () {
count = (count+1) % 200;
id.setOptions({
icons: [{
offset: (count/2)+'%'
}]
});
}, 20);
};
I tried another function like this but it didn't work at all:
function stopCircle(id) {
id.setOptions({
icons: [{
offset: '0%'
}]
};
Neither:
function stopCircle(id) {
id.setOptions({
icons: null
};
Thanks.
id is an index into your array of polylines. To access the polyline you need to use polylines[id] (i.e polylines[id].setOptions.
You probably also want to stop the timer, for that you need to keep a reference to the value returned by setInterval.
working example
function stopCircle(id) {
clearInterval(polylines[id].handle);
polylines[id].polyline.setOptions({
icons: null});
};
Where the polylines array now contains:
polylines[i] = new Object();
polylines[i].polyline = polyline;
polylines[i].handle = animateCircle(i);
For me "id" is a polyline itself.
All I need is to keep the output from "setInterval", that should be the input for "clearInterval".
These are the two functions:
function animateCircle(id) {
var count = 0;
window.clearInterval(id.offsetId);
id.offsetId = window.setInterval(function () {
count = (count+1) % 200;
id.setOptions({
icons: [{
offset: (count/2)+'%'
}]
});
}, 20);
};
function stopCircle(id) {
window.clearInterval(id.offsetId);
id.setOptions({
icons: null
});
};