JFreeChart et XYSplineRenderer

Fermé
bt33 Messages postés 5 Date d'inscription mardi 22 janvier 2013 Statut Membre Dernière intervention 25 janvier 2013 - Modifié par bt33 le 22/01/2013 à 18:42
 bt33 - 29 janv. 2013 à 10:05
Bonjour,
Je developpe une application java qui génère des graphiques avec JFreeChart. Je coince sur un type de graphique dont le rendu est différent de ce que je devrais avoir et le soucis proviendrait d'un mauvais usage du XYSplineRenderer. Ma courbe devrait être lissée, or des points semblent ajoutés et la courbe est saccadée.
Auriez vous une idée ? Merci de votre retour



8 réponses

Bonjour,
J'ai pu voir tes images (depuis chez moi!). J'ai malheureusement le même résultat alors que ce que je devrais obtenir est ceci :
<a href='http://imageshack.us/a/img94/4125/jfimageoriginale.png'>Image</a>

Quelque chose me dit que ça a l'air impossible.
En tout cas, merci de passer du temps sur mon problème.
1
KX Messages postés 16754 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 25 novembre 2024 3 020
27 janv. 2013 à 22:48
Le problème c'est que tu rebrousses chemin sur les X, en fait ça marcherais très bien en inversant les axes des X et des Y, c'est à dire avec ces quelques modifications :

plot.setRangeAxis(0, axeAbscisse);
plot.setDomainAxis(axeOrdonnee1);

dataset.addSeries(affecteSerie(tabOrdonnes, tabAbscisses, "maCourbe"));

Ce qui donne : JF_IMAGE_0.PNG, c'est quasiment ce que tu veux... si on tourne la tête ^^
J'avoue ne pas trop connaître jFreeChart, j'aide comme je peux, mais il y a sûrement moyen de faire pivoter automatiquement l'image, ou d'utiliser un autre rendu pour lisser la courbe malgré tout...
0
KX Messages postés 16754 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 25 novembre 2024 3 020
27 janv. 2013 à 23:20
Remarque : en fait l'inversion des axes se fait simplement en mettant PlotOrientation.HORIZONTAL à la création du jFreeChart (plutôt que de faire tous les changements précédents), je vais essayer de voir si on peut faire des petits changements simples comme ça et arriver au bon résultat...
0
Mais, avec les axes originaux, pourquoi la courbe s'affiche comme il faudrait (mais sans lissage) si on n'utilise pas de spline ?
0
KX Messages postés 16754 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 25 novembre 2024 3 020
28 janv. 2013 à 10:21
Sans le spline ça fonctionne bien, on a des points, on tire un trait entre deux points, et l'affichage donne ce qu'on veut, c'est primaire mais ça fonctionne.
Avec le spline on fait une régression polynomiale sur ces points, et pour que ça marche bien il faut que les abscisses se suivent, sinon ça donne un peu n'importe quoi parce que l'algortihme ne doit pas prévoir ce genre de cas. Donc deux solutions soit on ordonne les abscisses (ce qui était ma première idée) mais ça ne correspond pas vraiment à ce que tu cherches, soit on permute abscisses et ordonnées pour faire la régression sur les ordonnées, là ça donne bien ce que tu veux sauf qu'il faudrait réussir à re-permuter les axes une fois la régression effectuée.

Pour l'instant, le mieux que j'ai trouvé c'est donc faire cette petite modification d'orientation :

public void creation() throws IOException{ 

jFreeChart = ChartFactory.createXYLineChart("", "", "", dataset, PlotOrientation.HORIZONTAL, false, false, false ); 
0
En effet avec le PlotOrientation.HORIZONTAL, cela fonctionne presque.
Grace à toi j'ai trouvé une version expérimentale de JFreechart qui me donne presque le rendu que j'attends. Je vais essayé d'améliorer mon résultat et je te montrerai une image finale.
Le code que j'ai trouvé recalcule des points et joue en effet sur l'orientation du graphique.
Merci
0
KX Messages postés 16754 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 25 novembre 2024 3 020
22 janv. 2013 à 19:00
Est-ce que tu aurais un bout de code pour tester ?
0
bt33 Messages postés 5 Date d'inscription mardi 22 janvier 2013 Statut Membre Dernière intervention 25 janvier 2013
24 janv. 2013 à 10:45
Bonjour,
Voici du code simplement recupéré du code de l'appli qui reproduit mon pb (il genere une image jfreechart sur D:) !

Merci

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.renderer.xy.XYSplineRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.RectangleInsets;


public class MonSpline {

private XYSeriesCollection dataset = new XYSeriesCollection();
private JFreeChart jFreeChart;
private XYPlot plot;

public void creation() throws IOException{

jFreeChart = ChartFactory.createXYLineChart("", "", "", dataset,
PlotOrientation.VERTICAL, false,
false,
false
);
plot = (XYPlot) jFreeChart.getPlot();
plot.setBackgroundPaint(Color.white);
plot.setOutlineVisible(true);


addRepere();
affecteData();

XYLineAndShapeRenderer renderer2 = new XYSplineRenderer();
renderer2.setSeriesShapesVisible(0, false);
BasicStroke newStroke = new BasicStroke((float) (4.0));
renderer2.setSeriesStroke(0, newStroke);
renderer2.setSeriesPaint(0, java.awt.Color.black);
renderer2.setSeriesVisibleInLegend(0,false);
plot.setRenderer(0, renderer2);

// Creation de l'image finale
BufferedImage bufferImage = jFreeChart.createBufferedImage(
1200,
1500, BufferedImage.BITMASK, null);
String fileJFreeChart = "D:/JF_IMAGE.PNG";

ChartUtilities.saveChartAsPNG(new File(fileJFreeChart), jFreeChart, 1200, 1500, null, false, 0);
}

/*
* Creation des axes
*/
public void addRepere() {
//Axe des abscisses
NumberAxis axeAbscisse = new NumberAxis("Axe 1");
axeAbscisse.setAxisLinePaint(Color.BLACK);
axeAbscisse.setLabelPaint(Color.BLACK);
axeAbscisse.setPositiveArrowVisible(true);
axeAbscisse.setNegativeArrowVisible(true);

axeAbscisse.setInverted(false);
axeAbscisse.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
axeAbscisse.setAutoRangeIncludesZero(false);

axeAbscisse.setRange(-1,18.0);
axeAbscisse.setVisible(true);
axeAbscisse.setTickUnit(new NumberTickUnit(Math.abs(1.0)));
axeAbscisse.setTickMarkOutsideLength(11.0f);// longueur du ticker

axeAbscisse.setMinorTickCount(9);
axeAbscisse.setMinorTickMarksVisible(true);
axeAbscisse.setMinorTickMarkOutsideLength(10.0f);

axeAbscisse.setTickMarkStroke(new BasicStroke(2.0f,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
axeAbscisse.setTickLabelPaint(Color.BLACK);
axeAbscisse.setTickLabelPaint(Color.BLACK);

plot.setDomainAxis(axeAbscisse);
plot.getDomainAxis().setVisible(true);


// axe vertical gauche
//---------------------------------------
NumberAxis axeOrdonnee1 = new NumberAxis("Axe 2");
axeOrdonnee1.setPositiveArrowVisible(true);
axeOrdonnee1.setNegativeArrowVisible(true);
axeOrdonnee1.setInverted(false);
axeOrdonnee1.setStandardTickUnits(NumberAxis.createIntegerTickUnits());

axeOrdonnee1.setAutoRangeIncludesZero(false);
axeOrdonnee1.setAxisLinePaint(Color.BLACK);
axeOrdonnee1.setLabelPaint(Color.BLACK);
axeOrdonnee1.setRange(0.0, 100.0);
axeOrdonnee1.setAutoRange(false);
axeOrdonnee1.setTickUnit(new NumberTickUnit(10.0));
axeOrdonnee1.setTickMarkOutsideLength(11.0f);
axeOrdonnee1.setTickLabelsVisible(true);
axeOrdonnee1.setTickLabelPaint(Color.black);
axeOrdonnee1.setMinorTickCount(4);
axeOrdonnee1.setMinorTickMarksVisible(true);
axeOrdonnee1.setMinorTickMarkOutsideLength(10.0f);
axeOrdonnee1.setTickMarkStroke(new BasicStroke(2.0f,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
axeOrdonnee1.setTickLabelInsets(new RectangleInsets(4, 15, 4, 4));
axeOrdonnee1.setTickLabelPaint(Color.BLACK);

plot.setRangeAxis(0, axeOrdonnee1);
plot.getRangeAxis().setVisible(true);
}

/**
* Crée les series
*/
void affecteData(){
final XYSeriesCollection dataset = new XYSeriesCollection();
Double[] tabAbscisses = new Double []{0.00644026, -0.0744425, 0.147181, 0.82995, 0.878902, -0.102915};
Double[] tabOrdonnes = new Double []{0.0, 10.0, 20.0, 30.0, 40.0, 50.0};
dataset.addSeries(affecteSerie(tabAbscisses, tabOrdonnes, "maCoubre"));
plot.setDataset(0,dataset);
}

/**
* Valorise les XYSeries
* @param tabAbsisses
* @param tabOrdonnes
* @param name
* @return
*/
public XYSeries affecteSerie(Double[] tabAbsisses,Double[] tabOrdonnes, String name ){
XYSeries series = new XYSeries(name, false, true);
for (int indexgrille = 0; indexgrille < tabAbsisses.length; indexgrille++) {
double abscisse = tabAbsisses[indexgrille];
double ordonnee = tabOrdonnes[indexgrille];
series.add(abscisse, ordonnee);
}
return series;
}
}
0
KX Messages postés 16754 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 25 novembre 2024 3 020
24 janv. 2013 à 16:41
Dans affecteData, les données devraient être dans l'ordre croissant des abscisses, pas des ordonnées, sinon ta courbe rebrousse chemin sur les X.

Voici comment trier tes tableaux automatiquement :

public static void trier(Double[] abs, Double[] ord)
{
	int n=abs.length;
	if (n!=ord.length)
		throw new IllegalArgumentException("Tailles incompatibles");
	
	Point2D.Double[] tab = new Point2D.Double[n];
	for (int i=0; i<n; i++)
		tab[i] = new Point2D.Double(abs[i], ord[i]);
	
	Arrays.sort(tab, new Comparator<Point2D.Double>()
	{
		public int compare(Point2D.Double d1, Point2D.Double d2)
		{
			return Double.compare(d1.x, d2.x);
		}		
	});
	
	for (int i=0; i<n; i++)
	{
		abs[i] = tab[i].x;
		ord[i] = tab[i].y;
	}
}

trier(tabAbscisses,tabOrdonnes);
dataset.addSeries(affecteSerie(tabAbscisses, tabOrdonnes, "maCourbe"));

Mais sinon, pour que ta courbe soit lisse il faut lui augmenter sa précision lors de sa construction :

renderer2 = new XYSplineRenderer(20);
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
bt33 Messages postés 5 Date d'inscription mardi 22 janvier 2013 Statut Membre Dernière intervention 25 janvier 2013
25 janv. 2013 à 10:12
Merci pour cette réponse.
Je vais la tester.
0
bt33 Messages postés 5 Date d'inscription mardi 22 janvier 2013 Statut Membre Dernière intervention 25 janvier 2013
25 janv. 2013 à 10:52
Il y a un léger mieux.
Mais la courbe que je devrais obtenir est particuliere (presque vertical)
Cette courbe part du point (-0.102915,50) et descend en ondulant jusqu'au point (6.44026E-03,0).
Elle descend de l'ordonnée 50, puis 40, 30, 20, 10 puis 0. (ou monte de 0 à 50)

Mon resultat est une courbe saccadée qui va du point en ordonnée 50 puis, 0, puis 20, 10 et 40 (avec abscisses croissants).

Si je remplace le XYLineAndShapeRenderer() par un XYLineAndShapeRenderer(), sans procéder à aucun tri, la courbe s'affiche correctement mais elle n'est pas lissée.
0
KX Messages postés 16754 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 25 novembre 2024 3 020
25 janv. 2013 à 11:35
"Si je remplace le XYLineAndShapeRenderer() par un XYLineAndShapeRenderer()"
Ce n'est pas clair ça...

La seule chose que j'ai faite, c'est remplacer le constructeur par défaut new XYSplineRenderer(); par le constructeur new XYSplineRenderer(20); qui spécifie une précision pour le lissage.

Donc au départ on avait ça : JF_IMAGE_1.PNG
Avec cette petite modification, on a bien lissage et ça donne ça : JF_IMAGE_2.PNG

"Elle descend de l'ordonnée 50, puis 40, 30, 20, 10 puis 0."
Ce n'est pas du tout ce que tu donnes dans le code :

Double[] tabAbscisses = new Double []{0.00644026, -0.0744425, 0.147181, 0.82995, 0.878902, -0.102915}; 
Double[] tabOrdonnes = new Double []{0.0, 10.0, 20.0, 30.0, 40.0, 50.0}; 

Donc moi j'ai fait un tri pour avoir les abscisses croissantes, ce qui donne,

X = [-0.102915, -0.0744425, 0.00644026, 0.147181, 0.82995, 0.878902]
Y = [50.0, 10.0, 0.0, 20.0, 30.0, 40.0]

Ce qui fait ça : JF_IMAGE_3.PNG

Mais ce n'est pas une descente 50,40,30,20,10,0... alors on pourrait le faire bien sûr, mais il faut le dire dans le code !

Double[] tabAbscisses = {-0.102915, -0.0744425, 0.00644026, 0.147181, 0.82995, 0.878902}; 
Double[] tabOrdonnes = {50.0, 40.0, 30.0, 20.0, 10.0, 0.0};

Ce qui donne ça : JF_IMAGE_4.PNG

Alors il y a effectivement un petit truc "ennuyeux", c'est cette boucle qui remonte alors qu'elle ne devrait peut-être pas, mais c'est parce que le XYSplineRenderer ne fais pas juste un lissage de la courbe, il essaye de deviner ce que devrait être la courbe, et ici il l'approche à équation d'ordre 3, ce qui donne ce petit sursaut.
0
bt33 Messages postés 5 Date d'inscription mardi 22 janvier 2013 Statut Membre Dernière intervention 25 janvier 2013
25 janv. 2013 à 13:29
Je ne peux pas visualiser tes images.
J'ai un officescan qui bloque...
0
KX Messages postés 16754 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 25 novembre 2024 3 020
25 janv. 2013 à 17:35
"J'ai un officescan qui bloque...". Encore un truc bien inutile...
J'ai mis les images ailleurs mais si ça bloque encore il doit bien y avoir un moyen de dire à ton soft d'accepter quand même ces sites (tu n'es pas en Corée du Nord quand même !)

JF_IMAGE_1. PNG / JF_IMAGE_2. PNG / JF_IMAGE_3. PNG / JF_IMAGE_4. PNG
0