I am developing a small JS app, which uses the amCharts library. I am using the latest version of amCharts 4, mainly the map part of the library.
The problem I have, is that I can't change the background color, when I use a LinePattern overlay over a certain country.
How it looks:
And this is how I want it to be. Basically set the background to a color of one country and the LinePattern to a color of second country.
How I want it to look:
I have tried changing the fill color on the MapPolygonSeries, the fill value, which adapter returns, but both without a success.
Here is the code, that I am creating this pattern.
"series": [{
"type": "MapPolygonSeries",
"useGeodata": true,
"exclude": ["AQ"],
"mapPolygons": {
"propertyFields": {
"fill": "fill",
"selected": "selected"
},
//The adapter, takes care of the pattern
"adapter": {
"fill": function (fill, target) {
if (target.dataItem.dataContext && target.dataItem.dataContext.selected) {
var pattern = new am4core.LinePattern();
pattern.width = 5;
pattern.height = 5;
pattern.stroke = am4core.color("#6f3094");
pattern.strokeWidth = 1;
pattern.rotation = 45;
return pattern;
}
return fill;
}
}
},
"data": [{
"id": "PL",
"fill": "#6f3094"
}, {
"id": "DE",
"fill": "#eb4034",
"selected": true,
}, {
"id": "SE",
"fill": "#228B22"
}]
}]
following the solution
var pattern = new am4core.LinePattern();
pattern.width = 5;
pattern.height = 5;
pattern.stroke = am4core.color("#6f3094");
pattern.strokeWidth = 1;
pattern.rotation = 45;
//Code added - start
pattern.backgroundFill = am4core.color("green");
pattern.backgroundOpacity = 1;
//Code added - end
return pattern;
I am trying to sort an array by multiple properties, but the problem is that my array is multidimensional.
Currently I have built this:
// Private function to get the value of the property
var _getPropertyValue = function (object, notation) {
// Get all the properties
var properties = notation.split('.');
// If we only have one property
if (properties.length === 1) {
// Return our value
return object[properties];
}
// Loop through our properties
for (var property in object) {
// Make sure we are a property
if (object.hasOwnProperty(property)) {
// If we our property name is the same as our first property
if (property === properties[0]) {
// Remove the first item from our properties
properties.splice(0, 1);
// Create our new dot notation
var dotNotation = properties.join('.');
// Find the value of the new dot notation
return _getPropertyValue(object[property], dotNotation);
}
}
}
};
// Create a service
var service = {
// Sorts our products
sort: function (products, notation) {
notation = notation || 'details.title';
// Call sort
products.sort(function (a, b) {
// Get our values
var aValue = _getPropertyValue(a, notation),
bValue = _getPropertyValue(b, notation);
console.log(bValue);
// If our attribute name is not the same as the second attribute
if (aValue <= bValue) {
// Return -1
return -1;
}
// Otherwise return 1
return 1;
});
}
};
// Return our service
return service;
And this is one item from the array (products)
{
"id": 1,
"gtin": "8714574627946|4549292038446",
"productId": "0592C022",
"make": "Canon",
"model": "750D + EF-S 18-55mm",
"expert": false,
"sponsored": false,
"attributes": {
"id": 1,
"compatibleMemory": "SD, SDHC, SDXC\"",
"whiteBalance": "ATW, Cloudy, Custom modes, Daylight, Flash, Fluorescent L, Shade, Tungsten\"",
"sceneModes": "Food, Landscape, Sports\"",
"shootingModes": "",
"photoEffects": "",
"cameraPlayback": "Movie, Single image, Slide show\"",
"tripod": false,
"directPrinting": false,
"colour": "Black",
"picture": {
"id": 1,
"megapixel": "24.2 MP",
"type": "SLR Camera Kit",
"sensorType": "CMOS",
"maxResolution": "6000 x 4000 pixels",
"resolutions": "3984x2656, 2976x1984, 1920x1280, 720x480, 5328x4000, 3552x2664, 2656x1992, 1696x1280, 640x480, 6000x3368, 3984x2240, 2976x1680, 1920x1080, 720x480, 4000x4000, 2656x2656, 1984x1984, 1280x1280, 480x480\"",
"stablizer": true,
"location": "Lens",
"supportedAspectRatios": "2.9 cm",
"totalMegapixels": "24.7 MP",
"formats": "JPG"
},
"video": {
"id": 1,
"maxResolution": "1920 x 1080 pixels",
"resolutions": "640 x 480, 1280 x 720, 1920 x 1080 pixels\"",
"captureResolution": "",
"frameRate": "",
"fullHD": true,
"supportedFormats": null
},
"audio": {
"id": 1,
"supportedFormats": ""
},
"battery": {
"id": 1,
"powerSource": "Battery",
"technology": "Lithium-Ion (Li-Ion)",
"life": "",
"type": "LP-E17"
},
"dimensions": {
"id": 1,
"width": "",
"depth": "7.78 cm",
"height": "10.1 cm",
"weight": "",
"weightIncludingBattery": "555 g"
},
"display": {
"id": 1,
"type": "LCD",
"diagonal": "7.62 cm (3\"\")\"",
"resolution": "1040000 pixels"
},
"exposure": {
"id": 1,
"isoSensitivity": "100, 6400, 12800, Auto\"",
"mode": "Auto, Manual\"",
"correction": "�5EV (1/2; 1/3 EV step)",
"metering": "Centre-weighted, Evaluative (Multi-pattern), Partial, Spot\"",
"minimum": 100,
"maxiumum": 12800
},
"flash": {
"id": 1,
"modes": "Hi-speed sync, Red-eye reduction\"",
"exposureLock": true,
"rangeWide": "",
"rangeTelephoto": "",
"rechargeTime": "",
"speed": "1/200"
},
"focusing": {
"id": 1,
"focus": "TTL-CT-SIR",
"adjustment": "",
"autoFocusModes": "",
"closestDistance": "0.25 m",
"normalRange": "",
"macroRangeTelephoto": "",
"macroRangeWide": "",
"autoModeTelephoto": "",
"autoModeWide": ""
},
"interface": {
"id": 1,
"pictBridge": true,
"usbVersion": "2.0",
"usbType": "",
"hdmi": true,
"hdmiType": "Mini"
},
"lens": {
"id": 1,
"focalLength": "18 - 55 mm",
"minimumFocalLength": "2.9 cm",
"maximumFocalLength": "8.8 cm",
"minimumAperture": "3.5",
"maximumAperture": "38",
"lensStructure": "13/11",
"zoom": {
"id": 1,
"optical": "",
"digital": "",
"extraSmart": "",
"combined": ""
}
},
"network": {
"id": 1,
"wiFi": false,
"wiFiStandards": "",
"nfc": false
},
"shutter": {
"id": 1,
"fastestSpeed": "1/4000 s",
"slowestSpeed": "30 s"
}
},
"details": {
"id": 1,
"title": "Canon EOS 750D + EF-S 18-55mm",
"description": "\"<b>Take your pictures to the next level with EOS 750D</b>\\n- Effortlessly take your pictures to the next level with the latest DSLR technology and Scene Intelligent Auto mode.\\n- Effortlessly capture stunning detail in any situation\\n- Record cinematic movies as easily as you shoot stills\\n- Easily connect and share your images with the world\\n\\n<b>Take your pictures to the next level with EOS 750D</b>\\n<b>Range of shooting modes</b>\\nEffortlessly capture stunning images using the latest DSLR technology with Basic and Creative modes, which allow you to take as much or as little control as you like.\\n\\n<b>Moveable screen for creative framing</b>\\nExplore creative shooting angles and enjoy simple and intuitive access to controls using the 3.0\"\" (7.7cm) Vari Angle LCD touch screen\\n\\n<b>Intelligent Viewfinder</b>\\nEOS 750D features an Intelligent Viewfinder which gives a much enhanced shooting experience. As you look through the viewfinder you can more easily see the focus point and any active AF areas, also the shooting information is clearly displayed.\\n\\n<b>Effortlessly capture stunning detail in any situation</b>\\nCapture vivid, detailed, high-resolution images with better dynamic range, lower noise and excellent control over depth of field thanks to a 24.2 Megapixel APS-C sensor.\\n\\n<b>19 all cross-type AF points for accurate subject tracking</b>\\nKeep track of fast moving action thanks to a fast and accurate autofocus system comprising 19 cross-type AF points.\\n\\n<b>Fast processor for action</b>\\nA powerful DIGIC 6 processor delivers full resolution shooting at 5 fps � so you�ll never miss that decisive moment.\\n\\n<b>Great low light shots</b>\\nTake memorable low light pictures without using flash thanks to a large ISO sensitivity range of ISO 100-12800 (extendable to ISO 25600)\\n\\n<b>Record cinematic Full HD movies as easily as you shoot stills</b>\\nShoot superbly detailed Full HD movies with a cinematic feel thanks to DSLR control over depth of field. Record your movies in MP4 format for quicker online sharing and easier transfer to other devices.\\n\\n<b>Smoother results</b>\\nEasily shoot cinematic Full HD movies with Hybrid CMOS AF III to track movement and focus smoothly between subjects.\\n\\n<b>Empower your creativity with easy shooting modes</b>\\nLet the camera do the work for you and capture creative photos with ease using a range of Scene Modes\\n\\n<b>Creative movie modes</b>\\nExpand the range of shooting possibilities in movies with features like Miniature Effect in movie.\"",
"shortDescription": "\"22.3 x 14.9mm CMOS, 24.2 megapixels, 3:2, DIGIC 6, LCD, ISO 12800, Full HD Movie, USB, HDMI mini, SD/SDHC/SDXC, Black\"",
"summary": "\"Canon 750D + EF-S 18-55mm, EOS. Megapixel: 24.2 MP, Camera type: SLR Camera Kit, Sensor type: CMOS. Focal length range (f-f): 18 - 55 mm, Minimum focal length (35mm film equiv): 2.9 cm, Maximum focal length (35mm film equiv): 8.8 cm. Focus: TTL-CT-SIR, Closest focusing distance: 0.25 m. ISO sensitivity: 100, 6400, 12800, Auto, Light exposure modes: Auto, Manual, Light exposure control: Program AE. Fastest camera shutter speed: 1/4000 s, Slowest camera shutter speed: 30 s, Camera shutter type: Electronic\"",
"shortSummary": "\"Canon EOS 750D + EF-S 18-55mm, ATW, Cloudy, Custom modes, Daylight, Flash, Fluorescent L, Shade, Tungsten, Food, Landscape, Sports, Movie, Single image, Slide show, Battery, SLR Camera Kit, TTL-CT-SIR\""
},
"category": null,
"preview": {
"id": 1,
"highRes": "http://images.icecat.biz/img/norm/high/26171112-1991.jpg",
"lowRes": "http://images.icecat.biz/img/norm/low/26171112-1991.jpg",
"manual": ""
}
}
This works for 1 property. Does anyone know how I can efficiently rehash this to work with multiple properties?
I have tried to do this:
// Create a service
var service = {
// Sorts our products
sort: function (products, notations) {
// Call sort
products.sort(function (a, b) {
// For each notation
for (var i = 0; i < notations.length; i++) {
// Get our notation
var notation = notations[i];
// Get our values
var aValue = _getPropertyValue(a, notation),
bValue = _getPropertyValue(b, notation);
console.log(bValue);
// If our attribute name is not the same as the second attribute
if (aValue <= bValue) {
// Return -1
return -1;
}
// Otherwise return 1
return 1;
}
});
}
};
and invoked it like this:
handler.sort(self.products, ['attributes.dimensions.weightIncludingBattery', 'attributes.network.wiFi']);
but this only seems to sort by the first property and not the second.
With the link that #Nina Scholz posted I managed to create a set of functions that seem to work fast. The set of functions look like this:
// Private function to get the value of the property
var _getPropertyValue = function (object, notation) {
// Get all the properties
var properties = notation.split('.');
// If we only have one property
if (properties.length === 1) {
// Return our value
return object[properties];
}
// Loop through our properties
for (var property in object) {
// Make sure we are a property
if (object.hasOwnProperty(property)) {
// If we our property name is the same as our first property
if (property === properties[0]) {
// Remove the first item from our properties
properties.splice(0, 1);
// Create our new dot notation
var dotNotation = properties.join('.');
// Find the value of the new dot notation
return _getPropertyValue(object[property], dotNotation);
}
}
}
};
// Get our fields
var _getFields = function (notations) {
// Create our array
var fields = [];
// For each notation
angular.forEach(notations, function (notation) {
// Get our field
var names = notation.split('.'),
len = names.length,
name = names[len - 1];
// Push our name into our array
fields.push({ name: name, notation: notation });
});
// Return our fields
return fields;
};
// Create a mapped array
var _createMapped = function (array, notations) {
// Get our fields
var fields = _getFields(notations);
// Create our mapped array
var mapped = array.map(function (a, i) {
// Create our object
var obj = {
index: i
};
// For each of our fields
angular.forEach(fields, function (field) {
// Map our field
obj[field.name] = _getPropertyValue(a, field.notation);
});
// Return our object
return obj;
});
// Return our mapped array
return mapped;
};
// Create a service
var service = {
// Sorts our products
sort: function (products, notations) {
// Get our fields
var mapped = _createMapped(products, notations);
// Sort our mapped array
mapped.sort(function (a, b) {
// Loop through our properties
for (var i = 0; i < notations.length; i++) {
// Get our value (skip the first)
var o1 = a[i + 1];
var o2 = b[i + 1];
// Compare the values
if (o1 < o2) return -1;
if (o1 > o2) return 1;
}
// Default return
return 0;
});
// Get our result
var result = mapped.map(function (item) {
return products[item.index];
});
// Return our result
return result;
}
};
// Return our service
return service;
Basically you need something like that:
For the access to a property's value an iteration through the object
function getValue(string, object) {
return string.split('.').reduce(function (r, a) {
return r[a];
}, object);
}
And for the sort mechanism the iteration over the wanted sort parameters. Actually I assume, that all values are strings.
// handler.sort
function sort(array, order) {
array.sort(function (a, b) {
var r = 0;
order.some(function (s) {
r = getValue(s, a).localeCompare(getValue(s, b));
return r;
});
return r;
});
}
The drawback of this is a very slow sorting, because of the lookup mechanism of a specific value.
A faster way would be sorting with map, where the map contains only the wanted values from the getValue
Sorting over multiple properties can be done as in the following example
var data = [
{
a : 10,
b : 24
},
{
a : 11,
b : 20
},
{
a : 12,
b : 21
},
{
a : 12,
b : 10
},
{
a : 10,
b : 12
},
{
a : 15,
b : 7
},
{
a : 10,
b : 18
}
]
var sortData = (arr, prop1, prop2) => arr.sort((p,c) => p[prop1] < c[prop1] ? -1: p[prop1] == c[prop1] ? p[prop2] <= c[prop2] ? -1 : 1: 1);
sorted = sortData(data,"a","b");
document.write("<pre>" + JSON.stringify(sorted,null,2) + "</pre>");
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I'm growing desperate with amCharts doing the simplest task. Well, at least what I think is simple. I want to create a normal line chart with "normal" data. What do I mean with normal? Well, having an array with my y values, and maybe also one for x:
x = [1,2,3,4]
y = [5,6,7,8]
Shouldn't it be pretty easy to put something like this in a chart? And still I'm sitting here having no idea how to do this properly(!) in amcharts. I'm using Flask (Python) and can use jsonify to get my arrays into JSON files and send it to the client, but I'm not able to go any further.
Any ideas? I mean, shouldn't this be the most basic task? But all examples using amCharts doing special stuff with date formats for x..
Thank you very much!
First things first, if you are looking at plotting a line graph out of arbitrary X and Y coordinates (as opposed plotting series-based data), you're better off using XY chart than serial one.
Also, amCharts will not be able to plot data in separate arrays of coordinates.
You can either convert the data to a proper format at the moment of generation of data, so it looks like this:
chartData = [ {
x: 1,
y: 5
}, {
x: 2,
y: 6
}, {
x: 3,
y: 7
}, {
x: 4,
y: 8
}];
Or add some code that converts your data to amCharts-compatible data before supplying it to the chart:
/**
* Source data
*/
var x = [1,2,3,4];
var y = [5,6,7,8];
/**
* Convert source data to AmCharts-compatible format
*/
var chartData = [];
for( var i = 0; i < x.length; i++ ) {
chartData.push( {
"x": x[ i ],
"y": y[ i ]
} )
}
/**
* Create the chart
*/
var chart = AmCharts.makeChart( "chartdiv", {
"type": "xy",
"pathToImages": "http://www.amcharts.com/lib/3/images/",
"dataProvider": chartData,
"graphs": [ {
"bullet": "circle",
"bulletSize": 8,
"lineAlpha": 1,
"lineThickness": 2,
"fillAlphas": 0,
"xField": "x",
"yField": "y",
} ]
} );
Here's a working demo of the above:
http://codepen.io/amcharts/pen/15b2c710357a7e29eda11dc5caa07d44
following is the simpliest definition possible. The dateProvider is an array of objects where each element represents the x-axis item with it's y values.
AmCharts.makeChart("your element id",{
"type": "serial",
"pathToImages": "http://cdn.amcharts.com/lib/3/images/",
"categoryField": "category",
"graphs": [
{
"valueField": "column-1"
}
],
"dataProvider": [
{
"category": "1",
"column-1": "8"
},
{
"category": "2",
"column-1": 6
},
{
"category": "3",
"column-1": 2
},
{
"category": "4",
"column-1": 1
}
]
});
Live sample:
http://live.amcharts.com/2JmYT/edit/
I have the following array (here shown as JSON):
[{
"value": -1,
"absolute": false,
"callTime": 0
}, {
"value": 23,
"absolute": true,
"callTime": 1365179295887
}, {
"value": 1,
"absolute": false,
"callTime": 0
}, {
"value": 1,
"absolute": true,
"callTime": 0
}]
I need to sort this array by putting the objects with the highest values of the callTime property at the top of the array.
I use the following code (based on the explanation of the subject offered by MDN):
var handlers = JSON.parse("[...]");
handlers.sort(function(firstHandler, secondHandler) {
if (firstHandler.callTime < secondHandler.callTime) {
return -1; // sort firstHandler to a lower index than secondHandler.
} else {
return 1; // sort secondHandler to a lower index than firstHandler.
}
return 0;
});
console.log(JSON.stringify(handlers));
After running the function, I get the following output:
[{
"value": 1,
"absolute": true,
"callTime": 0
}, {
"value": 1,
"absolute": false,
"callTime": 0
}, {
"value": -1,
"absolute": false,
"callTime": 0
}, {
"value": 94,
"absolute": true,
"callTime": 1365179553381
}]
Which seems to be the inverse of what I'm expecting (notice how the only object with callTime different than zero is at the bottom).
I think there I might be missing something big here, or maybe I'm just misled, but also changing the body of the function to:
return firstHandler.callTime - secondHandler.callTime
should give the correct results, yet it doesn't seem to.
What could I be doing wrong?
Your sort function is incorrect. The logic is reversed, because your sort function is indicating that elements with smaller values of callTime come first, whereas you want larger values to be at the top of the array. Also, you always return -1 or 1, and never 0. It should return 0 when the elements are tied.
Rewrite it as follows:
handlers.sort(function(firstHandler, secondHandler) {
if (firstHandler.callTime > secondHandler.callTime) {
return -1; // sort firstHandler to a LOWER index than secondHandler.
} else if (firstHandler.callTime < secondHandler.callTime) {
return 1; // sort secondHandler to a LOWER index than firstHandler.
}
return 0; // sort firstHandler and secondHandler as equal
});
im using the current jquery ui slider with a range:
http://jqueryui.com/slider/#range
and the underscore.js
http://underscorejs.org/
so ive a minimum and a max which i send, after the user stopped to slide to a function:
currentSlide:function(){
$('#slider').slider({
range: true,
min: min,
max: max,
values: [ vmin, vmax ],
stop:$.proxy(this.afterSlide,this)
});
},
afterSlide:function(event, ui) {
console.log(ui.values[0]);
},
in the afterSlide function i get the min/max correctly, but i dont know how to use underscore.js to get all entries which have a price starting by this min and end by the max.
Examplearray:
var sample = {
"response": {
"things": [{
"index": 0,
"price": "10"
},{
"index": 1,
"price": "15"
},{
"index": 2,
"price": "60"
},{
"index": 3,
"price": "10"
},{
"index": 4,
"price": "100"
}
]
}
};
After im using the Slider i have the min/max [12,61] so i want to see all entries ONLY starting with a price between 12 and 61 $
var result = _.find(sample.response.things, min/max);??
I dont understand the _.range() function correctly to use it how i need, or to create a correct statement with the .where/.find() function.
I'd just get the max and min values, and since this seems rather trivial I'd just iterate the object and check if the relevant value is within the range, and add it to a new object, no _underscore needed really, something like this:
afterSlide:function(event, ui) {
var min = ui.values[0],
max = ui.values[1],
items = {};
$.each(sample.response.things[0], function(key, val) {
if (val.price > min && val.price < max) {
items[val.index] = val.price;
}
})
},